FaisalAhmed
FaisalAhmed

Reputation: 3651

How to get specific return type from Generic

I have class Attribute

class Attribute{
private Parser parser;
private String value;
private ParserType parseType;
}

and Enum

Enum ParserType{
STRING,NUMBER,RANGE
}

and parser interface

interface Parser<S,T>{
 T parse(S s){
}
}

now for based on different data type i am parsing value in different way. So for that i created classes

class StringParser implements Parser<String,String>{

@override
public String parse(String value){
return //... parse and return string
}

In similar way i have other parser

 class DoubleParser implements Parser<String,Double>{

@override
public Double parse(String value){
return //... parse and return double
}

What i am doing here is having reference of Parser in Attribute and setting different type of parser based on its ParserType

for example

class Attribute{
private Parser parser;
private String value;
private ParserType parseType;

Attribute(String value,ParserType parsertype){
 this.value=value;
 this.parserType=parserType
 }

private void setParserType()
 {
 switch(parserType){
  case ParserType.String:
   parser=new StringParser();
  break;

  case ParserType.Number:
  parser=new NumberParser();
  break;
 }

 }

  public void parse(){
  parser.parse(value); // here return type is object...  and i want it to be String if it is StringParser and bouble if it is DoubleParser
  }
}

if you see parse() method in Attribute class. I want it to give String data type if it is StringParser and double if it is DoubleParser

I know i am doing something wrong there using generics. So my question is What is the best practice to achieve this?

UPDATED

Thanks for Answer guys.. But Now i want to store that Attribute in a Map but when i do something like this it return me Object

// I did changes in my Attribute class 
public T parse(){
return parser.parse(value); // here return type is object...  and i want it to be String if it is StringParser and bouble if it is DoubleParser
}

Map<String,Attribute> map;
//assuming my map has some entries
for(Map.Entry<String,Attribute> m:map.entrySet()){
 map.getValue().parse(); // this parse methods returns object to me here. I want it to be some specific type like String or Double
}

Upvotes: 0

Views: 219

Answers (2)

Sneh
Sneh

Reputation: 3757

You can do something like this.

class Attribute<T, R> {

    private final T value;

    private final Parser<T, R> parser;

    public Attribute(T value, Parser<T, R> parser) {
        this.value = value;
        this.parser = parser;
    }

    private R parse() {
        return parser.parse(value);
    }

    private interface Parser<T, R> {
        R parse(T t);
    }

    private static class StringParser implements Parser<String, String> {
        @Override
        public String parse(String s) {
            return s;
        }
    }

    private static class DoubleParser implements Parser<String, Double> {
        @Override
        public Double parse(String s) {
            return Double.parseDouble(s);
        }
    }

    public static void main(String[] args) {
        Attribute<String, String> stringAttribute = new Attribute<>("input", new StringParser());
        Attribute<String, Double> doubleAttribute = new Attribute<>("1.0", new DoubleParser());

        System.out.println(stringAttribute.parse()); //should print the string
        System.out.println(doubleAttribute.parse()); //prints the double value
    }
}

Note - I have added all the interfaces and classes in the same class to keep the example in a single file for you to copy and run without creating multiple files.

I also changed the structure a little to make it more readable. You can provide the parsers during construction of the attribute class rather than keeping the ParserType in the class and then deciding what parser to instantiate. Attribute class should not be responsible for doing that.

Upvotes: 1

Vicky Thakor
Vicky Thakor

Reputation: 3916

Provide your return type while creating object of Attribute.java

Parser.java

interface Parser<S,T>{
     T parse(S s);
}

StringParser.java

public class StringParser<S> implements Parser<String, S>{
    @Override
    public S parse(String s) {
        return (S) "Hello";
    }
}

DoubleParser.java

public class DoubleParser<S> implements Parser<Double, S>{
    @Override
    public S parse(Double s) {
        return (S) new Double(1);
    }
}

Attribute.java

public class Attribute<T, R> {
    public T value;
    private Parser<T, R> parser;

    public Attribute(T value, String parserType) {
        switch (parserType) {
        case "ParserTypeString":
            parser = (Parser<T, R>) new StringParser<R>();
            break;
        case "ParserTypeNumber":
            parser = (Parser<T, R>) new DoubleParser<R>();
            break;
        }

    }

    public R parseReturn() {
        return parser.parse(value);
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        Attribute<String, String> str = new Attribute<String, String>("World", "ParserTypeString");
        System.out.println(str.parseReturn());

        Attribute<Double, Double> dbl = new Attribute<Double, Double>(5D, "ParserTypeNumber");
        System.out.println(dbl.parseReturn());
    }
}

Upvotes: 1

Related Questions