Java Raw Type and Generics Interaction

Java Generics new raw type vs generic type

You could make a raw list.

List list = new ArrayList();

That is fine, you now have a raw list, that you can put any object into. If you want to assign that list to a typed version, you will make an unchecked assignment.

List<String> strings = list;

That is because, what was put into list was never checked to be a string.

Difference between parameterized and raw type instantiation in java

The issue with

Alpha<CK, CV> alpha = new Alpha();

is that on the left hand side, you are using the generic type Alpha where on the right side you are using the raw type Alpha. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unless you absolutely have to.

As far as your original example of Alpha<CK, CV> alpha = new Alpha(), the compiler generates a warning for that assignment because it must. Consider this:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

Generics exist to provide compile-time protection against doing the wrong thing. In the above example, using the raw type means you don't get this protection and will get an error at runtime. This is why you should not use raw types.

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

Java Generics and Raw Types

Generic types are erased during compilation. So at runtime, an ArrayList is a raw ArrayList, no matter if you defined it as generic or not.

In your case, the code compiles as your ArrayList declaration is not generic, and it runs fine because of type erasure.

Why does the Java Compiler complain on using foreach with a raw type?

The difference is that when you use the raw type, all the generic references within the member signatures are converted to their raw forms too. So effectively you're calling a method which now has a signature like this:

List getList()

Now as for why your final version compiles - although it does, there's a warning if you use -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion
List<String> list = generics.getList();
^

This is similar to:

 List list = new ArrayList();
List<String> strings = list;

... which also compiles, but with a warning under -Xlint.

The moral of the story: don't use raw types!

Using Raw types to dynamically apply type parameters for a reflected subclass

Java have type erasure, so your Map<A,B> in runtime is just a Map, same for CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> is just a CrossReferenceImplementation.

This also means that you can cast any map to Map and just put any objects you want in it, so you can have Map<String, Long> that is actually storing objects of Map<Cookie, Fish> type, and this is why you need to be careful with raw types and reflections.

You can't really use reflection and generics normally - you will always have some unchecked code then, but you can limit it to minimum and make it kind of type-safe anyways.

Like you can create own method to get field: (this is a bit of pseudocode, I will skip all possible exceptions, etc)

public class FieldAccessor<O, T> { 
final Field field; // + private constructor
public T get(O object) { return (T) field.get(object); } // unsafe, bu we validated this before constructing this accessor
public static <O, T> FieldAccessor<O, T> create(Class<? super O> definingClass, Class<? super T> fieldClass, String fieldName) {
Field field = definingClass.getDeclaredField(fieldName);
if (field.getType() != fieldClass) {
throw some exception;
}
return new FieldAccessor<>(field);
}

Then you have all the needed validation before you need to use that field, and it will already return valid type. So you can get some value of valid type and add it to normal generic Map instance.

FieldAccessor<X, A> keyAccessor = FieldAccessor.create(X.class, A.class, "someProperty");
FieldAccessor<Y, B> valueAccessor = FieldAccessor.create(Y.class, B.class, "someOtherProperty");
Map<A, B> myMap = new HashMap<>();
mapMap.put(keyAccessor.get(myXValue), valueAccessor.get(myYValue));

This way you have type safe code that still works on reflections - it might still fail at runtime if you will provide invalid types, but at least you always know where it will fail - as here FieldAccessor is already checking all the types in runtime to ensure that you will not do something stupid like add Integer to Map<String, Long> as this might be hard to debug later. (unless someone will use this accessor as raw type, as .get isn't validated - but you can add that by passing definingClass to constructor and checking object instance in get methods)

You can do similar stuff for methods and fields that use generic types (like field of Map<X, Y> type, this FieldAccessor would only allow you to check if it is some kind of Map) - but it would be much harder as API for generics is still a bit "empty" - there is no build in way to create own instances of generic types or to check if they are assignable. (libraries like gson does that so they can deserialize maps and other generic types, they have own implementation of java generic type representation interfaces, like that ParameterizedType and implemented own method to check if given types are assignable)

Just when you are using reflections you need to always remember and understand that you are the one responsible for validating types, as compiler can't help you here, so that unsafe and raw typed code is fine as long as you have logic that validates if this code will never do something really unsafe (like that passing wrong type to generic method, like Integer to map of Long).

Just don't throw raw types and reflections in the middle of some normal code, add some abstraction to it, so it will be easier to maintain such code and project.

I hope this somewhat answers your question.

Java Generics: Accessing Generic Type at runtime

It is true that generics aren't generally known at runtime in Java, because they are implemented with Type Erasure.

Reflecting Generics?

However, you can stil extract some valuable information about the declared types (NOT the runtime objects' types), as presented in Ian Roberston's article Reflecting Generics and Prenkov's article Java Reflection: Generics.

Background on Generics and Type Erasure

Generics where introduced while conserving backwards compatibility at the source qnd binary level, hence some of their limitation, like:

  • the impossibility to have a short-hand form without at least some indicator for generics support (here, the so-called diamond operator <>),
  • the impossibility to inspect generic-types at runtime, because they had to be implemented with Type Erasure.

Further Reading

  • From The Java Tutorial:

    • section on Generic Types
    • section on Type Inference and Instantiation of Generic Classes
  • From the Java Language Specifications (JLS):

    • Java SE 5's JLS section on Types, Values and Variables
    • Java SE 7's JLS section on Types, Values and Variables
  • From good StackOverflow questions:

    • Java Raw Type and generics interaction
  • Others:

    • IBM Developer Series: Java Theory and Practice: Generics Gotchas (especially the sections The Road Not Taken, Generifying Existing Classes and Implications of Erasure).

Method references to raw types harmful?

There is no such thing as a “raw method reference”. Whilst raw types exist to help the migration of pre-Generics code, there can’t be any pre-Generics usage of method references, hence there is no “compatibility mode” and type inference is the norm. The Java Language Specification §15.13. Method Reference Expressions states:

If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.

Method reference expressions are always poly expressions

So while you may call the type before the :: a “raw type” when it referes to a generic class without specifying type arguments, the compiler will still infer the generic type signature according to the target function type. That’s why producing a warning about “raw type usage” makes no sense here.

Note that, e.g.

BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;

can be compiled with javac without any warning (the specification names similar examples where the type should get inferred), whereas

Function<Thread.State,String> f3 = Enum::name;

generates a warning. The specification says about this case:

In the second search, if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P2, ..., Pn. If ReferenceType is a raw type, and there exists a parameterization of this type, G<...>, that is a supertype of P1, the type to search is the result of capture conversion (§5.1.10) applied to G<...>;…

So in the above example, the compiler should infer Enum<Thread.State> as the parametrization of Enum that is a supertype of Thread.State to search for an appropriate method and come to the same result as for the f2 example. It somehow does work, though it generates the nonsensical raw type warning.


Since apparently, javac only generates this warning when it has to search for an appropriate supertype, there is a simple solution for your case. Just use the exact type to search:

public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}

This compiles without any warning.

In Java, how can I avoid raw types when calling getClass on an instance of a generic type?

When generics were first introduced, getClass returned Class<? extends X>, where X was the static type of the expression on which it was called. This behavior led to unreasonable compilation issues, as reported in this Oracle bug. Here is that bug report's example:

The following program fragment fails to compile

void f(List<Integer> li, List<String> ls) {
if (li.getClass() == ls.getClass())
;
}

because the intersection of Class<List<Integer>> and
Class<List<String>> is empty.

This issue was resolved by widening the return type of getClass to be what it is now. From the documentation:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

This resolved the above issue but consequently led to the issue that your question points out. Not long after, another bug was reported, arguing the following:

I think the getClass() typing rule could be changed to Class<? extends
wildcard(T)>

The wildcard operation is defined by: if T is parametrized,
wildcard(T)=erasure(T)<?> else ,
wildcard(T)=T

JUSTIFICATION :

  1. This rule introduce a raw type. Raw type must ONLY be used to interact with legacy code.

  2. The new Rule introduce a wildcard. Relationship between parametrized type and wildcard are based on subtyping rules. Relationship between parametrized type and wildcard are based on raw type conversion.

This bug was not acted upon and remains open to this day, with the following counterarguments:

The proposal means that getClass() would return a Class<? extends
ArrayList<?>>
object, which is incompatible with other Class<? extends
ArrayList<?>>
objects. This is compatible with existing code like:

List<String> l = ...;
Class<? extends List> c = l.getClass();

because the new type of the RHS, Class<? extends List<?>>, is a subtype of
Class<? extends List>.

A disadvantage of enriching Class's type argument is that it will
break idiomatic use of Class.cast. Today, you can write:

List<Integer> x = ...;
Class<? extends List> cl = x.getClass();
List<Integer> y = cl.cast(null);

and get a warning at cast(), because of the unchecked conversion from List to List<Integer>. But with the proposal, the analogous code doesn't compile:

List<Integer> x = ...;
Class<? extends List<?>> cl = x.getClass();
List<Integer> y = cl.cast(null);

because List<?> returned by cast() cannot be converted
to List<Integer>. The only way to avoid the error is to cast
cl.cast(..) up to List and suffer the unchecked conversion warning to
List<Integer>. This is effectively what getClass() does already.

Overall, the proposal seems like a good idea, but it has moderate
complexity and a fairly small payoff.

(abridged and with some typos corrected)



Related Topics



Leave a reply



Submit