Black_Rider
Black_Rider

Reputation: 1575

How to use Java Generics for returing T from String

I am trying to use Java generics for my particular use case. There is a Generic Class Node and there will be a lot of implementing class like NodeBoolean in this example.

public abstract class Node<T>
    {
        T defaultValue;

        public Node(T defaultValue) {
            this.defaultValue = defaultValue;
        }

        protected abstract T parse(String input) ;
        public T getValue()
        {
            try
            {
                // here will be some logic to get String value from some db or any other source
                String input = "true";
                return parse(input);
            } catch (Exception e) {
                return defaultValue;
            }
        }
    }

Implementing Class

class NodeBoolean extends Node<Boolean>
{
    public NodeBoolean(Boolean defaultValue) {
        super(defaultValue);
    }

    @Override
    protected Boolean parse(String input) {
        return Boolean.parseBoolean(input);
    }
}

Test Class

class TestNode
{
    public static void main(String[] args) {
        NodeBoolean node =  new NodeBoolean(true);
        System.out.println(node.getValue());
    }
}

Problems I am facing with this approach

  1. Suppose there are multiple variants of Constructor of class Node then all Implementing class also need to define them for visibility. This problem can be solved using Builder pattern but I am just wondering other possible solutions.

  2. There will be a lot of implementing class with having only one override method parse. I am looking for a way to define all different parsers based on instanceof of defaultValue in given example in generic class Node itself. And then this Node class not be abstract.

  3. I want to give functionality to define parse method which takes String and based on the user requirement return T value of it while creating object of Node class itself. By this way if I have only one occurrence of specific object type of Node can be created without defining any new Implementing class.

I am looking for a solution which solves above 3 problems. If I am not clear at any point then let me know.

Upvotes: 4

Views: 368

Answers (2)

Steve Chaloner
Steve Chaloner

Reputation: 8202

  1. I think exposing the constructors, or using the builder or factory patterns are the only possibilities here. But, this can be mitigated by the next answer.

  2. Instead of having the parse method as abstract, why not pass a function into the node that handles the parsing?


public class Node<T> {
    private T defaultValue;
    private Function<String, T> parser;

    public Node(T defaultValue,
                Function<String, T> parser) {
        this.defaultValue = defaultValue;
        this.parser = parser;
    }

    public T getValue()
    {
        // depending on the parser, re-parsing every time this is called could get expensive!
        try
        {
            // here will be some logic to get String value from some db or any other source
            String input = "true";
            return parser.apply(input);
        } catch (Exception e) {
            return defaultValue;
        }
    }
}

You can now create a Node by doing this

Node<Boolean> node = new Node<>(s -> Boolean.parseBoolean(s))

You could also set the default value in the parser function, removing the need to store it in the node - it depends on your requirements.

  1. See 2.

EDIT I've using Java 8's Function interface and lambdas here, but you can just as easily do this in Java 1.0 by defining your own Function interface.

public interface Function<I, O> {
    O apply(I);
}

and then implement this in some way - concrete class, anonymous class, etc - as required for the parsing you need to do.

Node<Boolean> node = new Node<>(new Function<String, Boolean>() {
    public Boolean apply(String s) {
        return Boolean.parseBoolean(s);
    }
});

EDIT 2

In response to the comment about pre-defined types, you can take a few different approaches.

public class NodeFactory {
    private static final Function <String, Boolean> BOOLEAN_PARSER = s -> Boolean.parseBoolean(s);
    .// plus more for integers, double, etc, as required

    private NodeFactory() {
        // no-op
    } 

    public static Node<Boolean> booleanNode(boolean defaultValue){
        return new Node<Boolean>(defaultValue,
                                 BOOLEAN_PARSER);
    }

    // plus more for integer, double, etc, as required
}

or you can take the extension route

public class BooleanNode extends Node<Boolean> {
    public BooleanNode(boolean defaultValue) {
        super(defaultValue,
              s -> Boolean.parseBoolean(s)); // or use a static parser as in the factory example
    }
}

In any case, keeping Node non-abstract allows for a lot of flexibility for your users, but your use case may not require that.

Upvotes: 3

tkokasih
tkokasih

Reputation: 1188

Do you have to extend the Node class? or just simply create an anonymous class? It is effectively "allow user to define parse method"

Node<Boolean> booleanNode = new Node<Boolean>(true) {
    @Override
    protected Boolean parse(String input) {
        return Boolean.parseBoolean(input);
    }
};

And maybe we can have a Factory class that will provide these Node classes:

class NodeFactory {
    public <T> static createNode(Class<T> classType) {
        if (classType == Boolean.class) {
            return newBooleanNode();
        } else if (classType == Integer.class){
            return newIntegerNode();
        }
    } 

    private Node<Boolean> newBooleanNode() {
        return new Node<Boolean>(true) {
            @Override
            protected Boolean parse(String input) {
                return Boolean.parseBoolean(input);
            }
        }
    }

    private Node<Integer> newIntegerNode() {
        return new Node<Integer>(0) {
            @Override
            protected Boolean parse(String input) {
                return Integer.parseInt(input);
            }
        }
    }
}

and in the client, we will have the following

Node<Boolean> booleanNode = NodeFactory.createNode(Boolean.class);
Node<Integer> integerNode = NodeFactory.createNode(Integer.class);

Upvotes: 0

Related Questions