Loss of security when using generics

I have a utility class (ListUtils) for deep copy lists. An important method in this class is copy, defined like this:

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 defined by:

public interface ICopy {
    <T extends ICopy> T copy() ;
}

      

Then, to force each AST node to implement ICopy, the IAstNode is defined like this:

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 dictates that I should write a copy with invalidation of the cancellation warnings?

Any ideas on what's going on?

+3


source to share


2 answers


In your interface, ICopy

you have created a copy

generic method , not the interface itself. This means that any implementation method must also be generic, with the same bounds, return type, and signature, to avoid compiler error and compiler warnings. You will need IAstNode

:

<T extends ICopy> T copy();

      

This is true even if you intended to T

be IAstNode

here. You get an immediate conversion warning with your current code because it T

might be IAstNode

. An untested conversion warning is a sign that you have lost type safety with your code, even though the compiler will allow you to compile the code.

What you can do to maintain type safety is move the type parameter declaration from the method copy

to the interface ICopy

.

public interface ICopy<T extends ICopy<T>> {
    T copy();
}

      

Then any subinterface (or implementing class) can provide a type argument or declare its own type parameter, which will be used to extend or implement the interface.



Looks here IAstNode

. It declares its own type parameter with its own bounds that match the bounds imposed ICopy

. It uses it as a type argument when expanding ICopy

. The method signature is now copy

simpler.

I gave a type parameter T

to type ImmutableList

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 indicate what actually is T

can do this:

public class MyAstNode implements IAstNode<MyAstNode>

      

The method copy

returns MyAstNode

and getChildren

returns ImmutableList<MyAstNode>

.

+3


source


You need to declare the type T

at the interface level

public interface ICopy<T extends ICopy>  {
    T copy() ;
}

      



Your copy method should be changed 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;
}

      

+1


source







All Articles