I've been learning Java 8 features, and so far have had success implementing them, but my latest code throws up two problems.
The first problem can be solved by casts, but this shouldn't happen here as the type inheritance should be sound; This is when accepting a one argument constructor parameter, where the constructor is a Function FunctionalInterface Method Reference.
Related is an issue where Java cannot resolve a concrete declared subtype for a generic parameter as valid, when populating a map with a Method Reference, if this type is used as the type of the constructor argument at invocation of the Method Reference.
The code that follows uses a Separated Interface inheritance tree, the interfaces specification, and the interfaces implementation, for two types of object Node, and Edge.
Edges have generic parameters to define the start Node and end Node specified when creating the method reference.
Both Nodes and Edges can have custom subtypes, which have a derived interface (subtype) implemented by the implementation subtype.
Nodes contain relationships as Sets which can be Concrete Node references, or Concrete edge references. (the startNode should match the parent object holding the set of Edges, but is required as a member of the edge, and this is beyond my control)
To avoid massive duplicated code, access is via an Operations interface
The Operations interface also has a separated interface and concrete implementation, and has two types (Node and Edge) to support the generics needed for Edges, one is created for each relationship set in each ImplExtendedNode which has relationships.
This modification is to move the storage of the creator method reference back into the class which knows which concrete types to use, and has permissions to access the Sets, rather than passing the custom Method Reference into each Operations constructor, and then passing it back for invocation.
The problem is very specific to the Edge (Constructor) Method Reference, and the inability correctly resolve that the types are valid and meet the requirements of the generic type constraints.
The system is part of a batch process. I've taken out everything unimportant, and I really appreciate helping me learn more about generics and java 8's newish features for anybody who understands what I have got wrong.
//Node Interfaces
public interface Node {
...
}
public interface DefaultNode extends Node {
...
public <S extends Object & DefaultNode> Optional<S> createRelationship(NodeOperations nodeOps);
public <R extends Object & DefaultEdge, T extends Object & DefaultNode> Optional<R> createRelationship(EdgeOperations edgeOps);
...
}
//Edge Interfaces
public interface Edge<S extends Object & Node, T extends Object & Node> {
...
}
public interface DefaultEdge<S extends Object & Node, T extends Object & Node> extends Edge<S, T> {
...
}
//Implementation Classes
//(base objects)
public abstract class ImplNode implements Node {
...
}
public abstract class ImplEdge<S extends Object & Node, T extends Object & Node> implements Edge<S, T> {
...
//constructor(s)
public ImplEdge(S s) {
...
}
...
}
//(provide basic functions)
public abstract class ImplDefaultNode extends ImplNode implements DefaultNode {
//holds method reference to custom node/edge constructors in child implementation class definitions (multiple per class)
protected Map<NodeOperations, Supplier> nodeSuppliers;
protected Map<EdgeOperations, Function> edgeSuppliers;
//this works fine
@Override
public <S extends Object & DefaultNode> Optional<S> createRelationship(NodeOperations nodeOps) {
...
Supplier<S> f = this.nodeSuppliers.get(nodeOps);
S relationship = f.get();
...
}
//issue one, cannot automatically cast type of "this" to expected type of T in f.apply(this) - I know this should be possible in most cases and usually points to an issue with the code
@Override
public <R extends Object & DefaultEdge, T extends Object & DefaultNode> Optional<R> createRelationship(EdgeOperations edgeOps) {
...
Function<T, R> f = this.edgeSuppliers.get(edgeOps);
R relationship = f.apply(this);
...
}
}
public abstract class ImplDefaultEdge<S extends Object & Node, T extends Object & Node> extends ImplEdge<S, T> implements DefaultEdge<S, T> {
private S startNode;
private T endNode;
...
}
//(provides custom extensions which rely on ImplDefaultNode)
//custom implementation of interface derived and defined from DefaultNode
public class ImplExtendedNode extends ImplDefaultNode implements ExtendedNode {
...
private Set<DifferentImplExtendedNode1> nodeRels1;
...
private Set<ImplExtendedEdge<ImplExtendedNode, DifferentImplExtendedNode2>> edgeRels1;
//this works when called via create function in superclass through generic map lookup
@Override
public NodeOperations morphNodeRels1() {
...
this.nodeSuppliers.put(i, DifferentImplExtendedNode1::new);
...
}
//this throws compiler error -
//Cannot convert java.lang.Object to ImplExtendedNode
//It only works if the first generic type is ImplNode, or ImplDefaultNode I think)
@Override
public EdgeOperations morphEdgeRels1() {
...
this.edgeSuppliers.put(i, ImplExtendedEdge<ImplExtendedNode, DifferentImplExtendedNode2>::new);
...
}
}
` can be simplified to `– user140547 Jun 22 '16 at 11:14`, making your code more readable.