I have a class called CacheRequestWrapper
that has one field with a generic type. It also has another field of type Optional<Long>
. When the class is made generic the value of this Optional field will no longer be of type Long
. Instead it will become of type Object
.
In other words, when I make the class generic the generic type of Optional
is erased and i don't understand why. Only the field cacheReturnValue
should be of type T
but it seems like the type T
is also assigned to Optional
. Let me explain why this seems to be the case with the example below:
public class CacheRequestWrapper<T> extends CacheWrapper {
private T cacheReturnValue;
private Optional<Long> requestingUserId;
public Optional<Long> getRequestingUserId() {
return requestingUserId;
}
public void setRequestingUserId(Optional<Long>requestingUserId) {
this.requestingUserId = requestingUserId;
}
//other Getters, setters
}
class Service{
//1.
private void myRawTypedMethod(CacheRequestWrapper cacheRequestWrapper) {
Long viewerId = cacheRequestWrapper.getRequestingUserId().get(); //This gives the compile error 'required type: Long, provided type Object'
}
//2.
private <T> void myGenericTypedMethod(CacheRequestWrapper<T> cacheRequestWrapper) {
Long viewerId = cacheRequestWrapper.getRequestingUserId().get(); //No compile error here.
}
}
In method 1.
I get a compile error when I'm assigning the optional value to viewerId
.
In generic method 2.
the compile error is not present. Its unclear why but what i think is happening is that the Long
type of Optional
is replaced by T
for some reason. This causes a problem because during runtime T
is not always of the same type as Long
.
So why is the generic type of the Optional
field overridden by the generic type T
?
CodePudding user response:
This behavior is specified in Section 4.8 of the Java Language Specification. A generic type without the type parameters is called a raw type, and in a raw type almost all generics are erased.
Raw types are only intended to interact with legacy Java code (version 1.4 or lower) which doesn't have generics, and to facilitate this interaction all generics need to be erased because the legacy code might want to do things which the generics would forbid.
For example, something like
String id = (String)wrapper.getRequestingUserId().orElseThrow();
would not compile if the generics were not erased. I admit, this is a strange example because Optional
was added to the Java language later than generics and would of not appear in legacy code, but Java does not make an exception for it.
Since pre-1.4 java code is becoming increasingly rare, new code should rarely use raw types.