简体   繁体   中英

Generic parameter if raw defaults to Object

I'm working on a Java 7 based project. I wanted to introduce a generic parameter on one of the classes, so that I can slowly eliminate the class casts required to make the code work.

Let's introduce a class, that is similar to the one I'm working on:

public class A {
   public List<B> getB() { ... }
}

B has lots of child classes, which when used, need to be casted, which is not ideal obviously. The modification I'd like to make is like this:

public class A<T extends B> {
    public List<T> getB() {...}
}

This can eliminate the casting required in some cases. However, A is used in a big part of the project, which makes it not too efficient to go through and rewrite every case it's used.

I hoped, that using the raw A class will make it so that getB() will return a type of B . The reality is that if used raw, getB() will return a type of Object instead. Is it some Java 7 related behavior, or am I overlooking something? Is there a way to make getB() return a type of B when A is raw? I didn't seem to come across this issue in Java 8, though I haven't really worked on a project this poorly structured either.

Edit: In the comments, I was requested for concrete code example, where the issue occurs. Assuming the above classes (edited slightly to better fit my actual scenario):

A a = new A();
for(B b: a.getB()) { // Compiler error, because `getB()` returns a list of `Object`s instead of `B`s.
    ...
}

A<B> a = new A<B>();
for(B b: a.getB()) { // Everything is fine here obviously
    ...
}

The problem is that if you use raw types, the type is erased from List as well, and you get a raw List type back from getB() . Because the forEach loop does not allow unchecked type conversions, you get a compilation error. With the disclaimer that raw types are best avoided , a simple workaround in your case is to use a temporary variable to do the type conversion:

A a = new A();
List<B> list = a.getB();
for( B b : list ) {
    System.out.println(b);
}

Or to emulate the enhanced for loop , in which case the unchecked type conversion will be permitted in the loop head:

for (Iterator<B> i = a.getB().iterator(); i.hasNext();) {
    System.out.println(i.next());
}

Your intention seems to be that, assuming a class C extends B , you could have an A<C> whose getB() method returns a List<C> . But if you allow old code to treat the method of the same object as returning List<B> , it could call getB().add(new B()) on it, which would subvert the entire type safety.

Of course, it might be that the old code never does that, perhaps, the list is even immutable, but the compiler's type system doesn't know.

Since you have to touch the code anyway, to benefit from the new generic type, you can extend the class, eg

public class A {
    public List<B> getB() { ... }
}

public class ExtendedA<T extends B> extends A {
    public List<B> getB() {
        return Collections.unmodifiableList(getT());
    }
    public List<T> getT() { ... }
}

You can only get the benefit of the new type parameter T for new or updated code, when an ExtendedA is created and consistently passed through the entire code path. But at any point where the ExtendedA is passed to old code, it continues to work as before, as long as the old code obeys the restrictions mentioned above. The code must not attempt to add B instances when not knowing the actual type parameter. But in this solution, the restriction is enforced at runtime by the unmodifiableList wrapper, so the generic type safety is retained, which is the reason why the signature of the wrapper method allows the type transition from List<T> to List<B> .

The best migration strategy would be to adapt all creation sites of A s as soon as possible, as then, the A class could become abstract, just serving as an interface for old code whereas ExtendedA is the actual type safe API and implementation for all new and updated code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM