When do Java generics require ? extends T instead of T and is there any downside of switching?
First - I have to direct you to http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html -- she does an amazing job.
The basic idea is that you use
<T extends SomeClass>
when the actual parameter can be SomeClass
or any subtype of it.
In your example,
Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));
You're saying that expected
can contain Class objects that represent any class that implements Serializable
. Your result map says it can only hold Date
class objects.
When you pass in result, you're setting T
to exactly Map
of String
to Date
class objects, which doesn't match Map
of String
to anything that's Serializable
.
One thing to check -- are you sure you want Class<Date>
and not Date
? A map of String
to Class<Date>
doesn't sound terribly useful in general (all it can hold is Date.class
as values rather than instances of Date
)
As for genericizing assertThat
, the idea is that the method can ensure that a Matcher
that fits the result type is passed in.
Stream Object instead of Stream ? extends T being produced when invoked on list
The "expected type: ... actual type: ..." message is not about list1.stream()
, it's about the value you return in the lambda in flatMap
. Observe where the red squiggles are:
It's saying that the code in the x -> ...
lambda is returning a Stream<Object>
, rather than the expected Stream<R>
.
Note that map(spendable -> x + "" + appendable)
can totally return a Stream<Object>
with the right type arguments ( if it were .<Object>map(appendable ->x + "" +appendable)
for example).
And in fact, the "expected type: ... actual type: ..." message is only produced by IntelliJ, so it's probably just IntelliJ being a bit silly. If you run javac
, you get a much better error message:
incompatible types: inference variable R#1 has incompatible bounds
.flatMap(x -> {
^
equality constraints: R#2
lower bounds: R#3,String
where R#1,T#1,R#2,T#2,U,R#3 are type-variables:
R#1 extends Object declared in method <R#1>flatMap(Function<? super T#1,? extends Stream<? extends R#1>>)
T#1 extends Object declared in interface Stream
R#2 extends Object declared in method <T#2,U,R#2>cartesian(List<? extends T#2>,List<? extends U>,BiFunction<? super T#2,? super U,R#2>)
T#2 extends Object declared in method <T#2,U,R#2>cartesian(List<? extends T#2>,List<? extends U>,BiFunction<? super T#2,? super U,R#2>)
U extends Object declared in method <T#2,U,R#2>cartesian(List<? extends T#2>,List<? extends U>,BiFunction<? super T#2,? super U,R#2>)
R#3 extends Object declared in method <R#3>map(Function<? super T#1,? extends R#3>)
That says that the type parameter R
in flatMap
cannot be inferred, because its bounds cannot be satisfied simultaneously. R
in flatMap
has to be the same as the R
in cartesian
, but its lower bounds are R
in map
, and String
.
Anyway, it seems like what you wanted to do was:
return list1.stream()
.flatMap(x -> list2.stream().map(y -> func.apply(x, y)));
java.lang.Class generics and wildcards
The subtyping relationship here is:
Class<? extends Iface>
╱ ╲
Class<? extends Iface<?>> Class<Impl>
(Which I explained in my answer to 'Cannot convert from List<List>
to List<List<?>>
'.)
So essentially it doesn't compile because it's a sideways conversion.
If it's possible, you can do the casting I described over there:
(Class<? extends Iface<?>>)(Class<? extends Impl>)Impl.class
If you can't do the cast, then you probably just have to deal with a raw bounded Class<? extends Iface>
. It's annoying primarily because of the warnings but it opens up the possibility for an error:
interface Iface<T> {
void accept(T a);
}
class Impl2 implements Iface<String> {
public void accept(String a) { }
}
class TestCase {
static Class<? extends Iface> clazz = Impl2.class;
public static void main(String[] args) throws Exception {
// throws ClassCastException
clazz.newInstance().accept(new Object());
}
}
Unlikely to happen, but it depends on what you're doing I suppose.
I tend to think this is a problem with the Java type system.
Possibly there should be a special rule that a type argument
? extends T<?>
contains a type argument? extends T
such that e.g. aClass<? extends T>
converts to aClass<? extends T<?>>
. This doesn't make sense from the perspective of the existing way that subtyping is defined (T
is a supertype ofT<?>
) but it makes sense from the perspective of type safety.Or e.g.
List.class
should be aClass<List<?>>
instead of aClass<List>
.Or some other clever thing people smarter than me can think up.
The interesting thing about the ClassCastException
I described above is that it's completely artificial. And in fact, preventing it with the unchecked cast causes a warning.
Just a sign that generics in Java are not done yet, I guess.
Whats the use of saying ? extends SomeObject instead of SomeObject
List<SomeObject> l;
In this you cannot say List<SomeObject> l = new ArrayList<SubClassOfSomeObjectClass>;
(not allowed)
wheres for
List<? extends SomeObject> l;
you can say
List<? extends SomeObject> l = new ArrayList<SubClassOfSomeObject>;
(allowed)
But note that in List<? extends SomeObject> l = new ArrayList<SubClassOfSomeObject>;
you cannot add anything to your list l because ? represents unknown class (Except null of-course).
Update: For your question in the comment What could I possibly do with a list if I cannot add anything to it?
Now consider a case in which you have to write a function to print your list but mind you it must only accept a List having objects which are subclasses of your SomeObject. In this case as I stated above you cannot use
public void printList(List<SubClassOfSomeObjectClass> someList)
So what would you do? You would do something like
public void printList(List<? extends SomeObject> someList) {
for(SomeObject myObj : someList) {
//process read operations on myObj
}
Class using generic in the exception throws and have a List String param doesn't compile
Let's cover ClassB
first.
Type arguments to a method is resolved by the caller of the method. It cannot be resolved by overrides in a subclass.
There are two ways to make ClassB
work. You can add the type argument there too.1
class ClassB implements Interface {
public <T extends Exception> void method(List<String> list) throws T{}
}
Or you can move the type argument up to the interface, so the subclass can decide.
interface Interface<T extends Exception> {
void method(List<String> list) throws T;
}
class ClassB implements Interface<Exception> {
public void method(List<String> list) throws Exception {}
}
As for why ClassA
work, it's because of the raw type in the argument. When you use a raw type, everything becomes raw, which means that when compiling ClassA
, Interface
looks like this raw type.
interface Interface {
void method(List list) throws Exception;
}
It is done that way for backwards compatibility. See JLS 4.8 Raw Types :
The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations.
It goes on to say:
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 generics 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.
1 I was actually wondering how the caller could resolve T
, i.e. how the compiler would infer what T
is, given that nothing in a method call would indicate the valid exceptions.
According to the Eclipse compiler, when you write the following, T
gets resolved as RuntimeException
.
new ClassB().method(null);
You can override that by explicitly giving the type, e.g. this will make the method throw a ParseException
.
new ClassB().<ParseException>method(null);
Follow-up
The inferred T
is RuntimeException
in Java 8. Without a catch statement, I get the following errors on earlier versions of Java:
1.5.0_22: unreported exception T; must be caught or declared to be thrown
1.6.0_45: unreported exception T; must be caught or declared to be thrown
1.7.0_79: error: unreported exception Exception; must be caught or declared to be thrown
Java 8 dedicated an entire chapter to type inference, and it says:
if the bound set contains
throws
αi, and the proper upper bounds of αi are, at most,Exception
,Throwable
, andObject
, then Ti =RuntimeException
.
Related Topics
Differencebetween Set and List
Spring MVC: How to Perform Validation
What Is the Best Java Image Processing Library/Approach
How to Respond with an Http 400 Error in a Spring MVC @Responsebody Method Returning String
How to Find Unused/Dead Code in Java Projects
Spring Resttemplate - How to Enable Full Debugging/Logging of Requests/Responses
Make Maven to Copy Dependencies into Target/Lib
Why Would a Static Nested Interface Be Used in Java
Polymorphism: Why Use "List List = New Arraylist" Instead of "Arraylist List = New Arraylist"
How to Access a Value Defined in the Application.Properties File in Spring Boot
How to Add a Filter Class in Spring Boot
Java 8 Lambdas, Function.Identity() or T->T
How to Convert Outputstream to Inputstream
Why Would One Mark Local Variables and Method Parameters as "Final" in Java