Reputation: 1575
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
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.
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
.
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
Reputation: 8202
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.
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.
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
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