I have an utility class (ListUtils) to deep copy lists. The important method in that class is copy, defined as follows:
public static <T extends ICopy> List<T> copy(List<T> xs) {
LinkedList<T> newList = new LinkedList<>() ;
for(T x : xs) {
newList.add(x.<T> copy()) ;
}
return xs;
}
The ICopy interface, in turn, is given by:
public interface ICopy {
<T extends ICopy> T copy() ;
}
Then, to force every AST node to implement ICopy, the IAstNode is defined as follows:
public interface IAstNode extends ICopy {
ImmutableList<? extends IAstNode> getChildren() ;
int getStartLine() ;
int getEndLine() ;
IAstNode copy() ;
}
At this point, I lose type safety, as the Java compiler indicates that I should write copy with a suppress warning annotations?
Any insights of what is going on?
In your ICopy interface, you have made the method copy generic, not the interface itself. This means that any implementing method must also be generic, with the same bounds, return type, and signature, to avoid a compiler error and a compiler warning. You would need in IAstNode:
<T extends ICopy> T copy();
This holds true even if you intended T to be IAstNode here. You get an unchecked conversion warning with your current code because T could be IAstNode. The unchecked conversion warning is the sign that you've lost type safety with your code, even if the compiler will let you compile the code.
What you can do to preserve type safety is to move the declaration of the generic type parameter from the copy method to the interface ICopy itself.
public interface ICopy<T extends ICopy<T>> {
T copy();
}
Then any subinterface (or implementing class) can supply a type argument, or it declare its own type parameter, to be used in extending or implementing the interface.
Here's what IAstNode looks like now. It declares its own type parameter with its own bounds, that fits the bounds imposed by ICopy. It uses it as a type argument when extending ICopy. The copy method signature is simpler now.
I gave the type parameter T to the ImmutableList type for getChildren; it makes the most sense in this context.
public interface IAstNode<T extends IAstNode<T>> extends ICopy<T> {
ImmutableList<T> getChildren() ;
int getStartLine() ;
int getEndLine() ;
T copy();
}
Any class that wants to specify what T actually is can do so:
public class MyAstNode implements IAstNode<MyAstNode>
The copy method would return a MyAstNode, and getChildren would return an ImmutableList<MyAstNode>.
You need to declare the type T on interface level
public interface ICopy<T extends ICopy> {
T copy() ;
}
Your copy method should be amended to
public <T extends ICopy<T>> List<T> copy(List<T> xs) {
LinkedList<T> newList = new LinkedList<>();
for (T x : xs) {
newList.add(x.copy());
}
return xs;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With