Combining Raw Types and Generic Methods

Combining Raw Types and Generic Methods

It's not exactly what you'd expect, but if you refer to a generic class in raw form, you lose the ability to use generics in any way for instance members. It's not restricted to generic methods either, check out this:

 public class MyContainer<T> {

public List<String> strings() {
return Arrays.asList("a", "b");
}
}

MyContainer container = new MyContainer<Integer>();
List<String> strings = container.strings(); //gives unchecked warning!

This is the relevant part of the JLS (4.8):

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

Java: Generic types inside Raw Type object or why my parametrization doesn't work

I'm going to make an assumption that your IType is actually a parameterized (and you've just confirmed it) type like so

public interface IType<E> {
public List<String> getAllItems();
}

In this case, if you declare a variable (or parameter) as

IType item;

you are using a Raw Type. With a variable of a raw type, all generic types in methods or fields accessed on that variable are erased. So

public List<String> getAllItems();

becomes

public List getAllItems();

and so the List Iterator will return references of type Object.

public void function(IType item) {
for (String str : item.getAllItems()) { // DOESN'T WORK! Incompoatible
// types. Required String,
// Found: Object
}
}

Combining Raw Types and Generic Methods

How to avoid raw type mixing generic inheritance and inner classes?

The problem is here :

public class Parent<T> extends GrandParent

Change it to

public class Parent<T> extends GrandParent<T>

in order for Parent and GrandParent to have the same generic type parameter.

Otherwise there is no relation between the generic type parameter T of Parent and the protected T t; member of GrandParent.

Java: Raw anonymous class loses generic types in its methods

Raw types exist for compatibility with code from before generics existed. They are treated as a full "opt-out" of all things generic. If you want generics, do not use raw types. That is the intent of the language design.

The solution here is to name the class. Yes, classes can appear inside methods and other statement blocks; such things are called local classes. An anonymous class is basically a local class without a name—and local classes can do anything anonymous classes can do.

String thisIsFine() {
var cell = new Object() { String s; }; // example local variable to show local classes can access them
class GenericImpl extends GenericClass<GenericImpl> {
@Override
protected void method() {
cell.s = getB(String.class);
}
}
new GenericImpl().method();
return cell.s;
}

Java generic methods in generics classes

'for backwards compatibility' seems a sufficient reason for the type erasure of class generic types - it is needed e.g. to allow you to return an untyped List and pass it to some legacy code. The extension of this to generic methods seems like a tricky sub-case.

The JLS snippet from 4.8 (which you quote) covers constructors, instance methods and member fields - generic methods are just a particular case of instance methods in general. So it seems your case is covered by this snippet.

Adapting JLS 4.8 to this specific case :

The type of a generic method is the raw type that corresponds to the
erasure of its type in the generic declaration corresponding to C.

(here the 'type' of the method would include all parameter and return types). If you interpret 'erasure' as 'erasing all generics', then this does seem to match the observed behaviour, although it is not very intuitive or even useful. It almost seems like an overzealous consistency, to erase all generics, rather than just generic class parameters (although who am I to second guess the designers).

Perhaps there could be problems where the class generic parameters interact with the method generic parameters - in your code they are fully independent, but you could imagine other cases where they are assigned / mixed together. I think it's worth pointing out that use of raw types are not recommended, as per the JLS :

The use of raw types is allowed only as a concession to compatibility
of legacy code. The use of raw types in code written after the
introduction of genericity into the Java programming language is
strongly discouraged. It is possible that future versions of the Java
programming language will disallow the use of raw types

Some of the thinking of the java developers is apparent here :

http://bugs.sun.com/view_bug.do?bug_id=6400189

(bug + fix showing that a method's return type is treated as part of the method's type for the purposes of this type erasure)

There is also this request, where someone appears to request the behaviour you describe - only erase the class generic parameters, not other generics - but it was rejected with this reasoning:

The request is to modify type erasure so that in the type declaration Foo<T>, erasure only removes T from parameterized types. Then, it so happens that within Map<K,V>'s declaration, Set<Map.Entry<K,V>> erases to Set<Map.Entry>.

But if Map<K,V> had a method that took type Map<String,V>, its erasure would just be Map<String>. For type erasure to change the number of type parameters is horrific, especially for compile-time method resolution. We are absolutely not going to accept this request.

It is too much to expect to be able to use raw types (Map) while still getting some of the type-safety of generics (Set<Map.Entry>).

Why am I still able to add any Object to a List with a raw-type reference type after changing its object type to a generic?

If you declare it as List<Long> you will get static compile time type checking. Do to type erasure the JVM does not know anything about those types at runtime.

List<Long> myList = new ArrayList<>();
myList.add("foo");

Will give a compilation error while:

public void breakGeneric(List list) {
list.add("foo");
}
....
List<Long> myList = new ArrayList<>();
breakGeneric(myList);

will add "foo" to a list no matter what type type is. Most IDEs will worn you about loosing the generic type.

Having the type in the new statement new ArrayList<Long>() would only have an effect if you chain off of that statement ie new ArrayList<Long>().add("foo"). That is the only way that a generic type only in the new statement will cause a compilation problem.

Java type erasure with generic methods

Generics are opt-in (because they were added in Java 5, and old code still needs to work).

ClassB sampleB1 = new ClassB();  // this is a raw type

When you opt-out by using raw types you won't get any of the features, including generic type inference (your <T> on the method will just be ignored). You will get a compiler warning about better not using raw types instead.


What does method return type <T> has to do with <O>?

Nothing. You declared <T> on the method, it is only visible for that method, it is completely unrelated to the O on the class (unless you did something like <T extends O>) and it could even shadow other things (you could call it <O> as well, or even <String> -- but don't do that).

Raw Type with List String gives compilation error

Can someone help me with the explanation of this issue?

Sure. It's following the JLS's description of raw types, in section 4.8:

To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type.

The erasure of GenericRaw<T> is:

public class GenericRaw {
public List get() { ... }
}

because:

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.

The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased.



Related Topics



Leave a reply



Submit