public abstract class Mother {
}
public class Daughter extends Mother {
}
public class Son extends Mother {
}
I need a Map which keys are one of Daughter or Son classes, and which values are lists of objects of one of those two classes, respectively.
For example:
/* 1. */ map.put(Daughter.class, new ArrayList<Daughter>()); // should compile
/* 2. */ map.put(Son.class, new ArrayList<Son>()); // should compile
/* 3. */ map.put(Daughter.class, new ArrayList<Son>()); // should not compile
/* 4. */ map.put(Son.class, new ArrayList<Daughter>()); // should not compile
I tried Map<Class<T extends Mother>, List<T>>, but it does not compile.
Map<Class<? extends Mother>, List<? extends Mother>> does compile, but the cases 3. and 4. do compile too while then shouldn't.
Is it even possible?
I don't think it's possible to encode this in the type, I'd do it with a custom class
class ClassMap<T> {
private Map<Class<? extends T>, List<? extends T>> backingMap = new HashMap<>();
public <E extends T> void put(Class<E> cls, List<E> value) {
backingMap.put(cls, value);
}
#SuppressWarnings("unchecked")
public <E extends T> List<E> get(Class<E> cls) {
return (List<E>)backingMap.get(cls);
}
}
It's safe to suppress warnings here as long as you don't leak the backingMap reference outside this class.
Assuming you're looking for a single map, no this is not possible.
Related
I'm really confused of how upper bounded types work in Java generics.
Let's say I have
interface IModel<T>
interface I
class A implements I
class B implements I
class C implements I
then I have a method with parameter as follows
foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
calling that method like
IModel<Map<A, Map<B, List<C>>>> model = ...
foo(model)
ends with compilation error
Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List<C>>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List<? extends I>>>>
I have read docs about Java generics from the Oracle web, trying to google it, but there must be something I totally misunderstood.
This question can be shorted as why
foo(IModel<List<? extends I>> dataModel)
can not accept argument like
IModel<List<A>> model
Explanation
List<A> is a subtype of List<? extends I>, so it is ok:
public void bar(List<? extends I> list);
List<A> listA;
bar(listA);
But, it does not make IModel<List<A>> a subtype of IModel<List<? extends I>>, just like IModel<Dog> is not a subtype of IModel<Animal>, so the code you posted can not be compiled.
Solution
You can change it to:
foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
or
<FIRST extends I, SECOND extends I, THIRD extends I> void foo(IModel<Map<FIRST, Map<SECOND, List<THIRD>>>> dataModel)
to make it compile.
First of all, I wonder how much effort it would have been for you (one person) to sort out the code to be in this form:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
{
}
}
instead of letting hundreds of people (who want to help you) do this on their own, in order to have something that they can compile and have a look at in their IDE. I mean, it's not that hard.
That being said: Technically, you're missing a few more extends clauses here. This compiles fine:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
{
}
}
But you should
not
implement it like that. That's obscure. Whatever this dataModel parameter is, you should consider creating a proper data structure for that, instead of passing along such a mess of deeply nested generic maps.
The reason of why the original version did not compile was already mentioned in other answers. And it can be made clearer by showing an example using a much simpler method call. Consider this example:
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
List<List<A>> lists = null;
exampleA(lists); // Error
exampleB(lists); // Works!
}
static void exampleA(List<List<? extends I>> lists)
{
}
static void exampleB(List<? extends List<? extends I>> lists)
{
}
}
The exampleA method cannot accept the given list, whereas the exampleB method can accept it.
The details are explained nicely in Which super-subtype relationships exist among instantiations of generic types? of the generics FAQ by Angelika Langer.
Intuitively, the key point is that the type List<A> is a subtype of List<? extends I>. But letting the method accept only a List<List<? extends I>> does not allow you to pass in a list whose elements are subtypes of List<? extends I>. In order to accept subtypes, you have to use ? extends.
(This could even be simplified further: When a method accepts a List<Number>, then you cannot pass in a List<Integer>. But this would not make the point of List<A> being a subtype of List<? extends I> clear here)
Having a method method1(Map<I> aMap>) and A being a class implementing I doesn't allow you to call the method with a Map<A> and that's for a reason.
Having the method:
public static void foo2(IModel<I> dataModel) {
System.out.println("Fooing 2");
}
Imagine this code:
IModel<A> simpleModel = new IModel<A>() {};
foo2(simpleModel);
This shouldn't work because you supply a more specific type to a method that requires a generic type. Now imagine foo2 does the following:
public static void foo2(IModel<I> dataModel) {
dataModel = new IModel<B>() {};
System.out.println("Fooing 2 after we change the instance");
}
Here you will try to set IModel to IModel which is valid - because B extends I, but if you were able to call that method with IModel it wouldn't work
Create your model like:
IModel<Map<I, Map<I, List<I>>>> model = ...
and in the corresponding maps and lists add objects of type A, B and C which will be valid and then call the function foo(model)
There is technique to define class, containing a method, returning a value of the same type as class -- self recursion.
But using this, is it possible to stop recursion?
Some code:
public class StopSelfRecursion {
// base class
static class Class1<Self extends Class1<Self,T>, T> {
public Self getMyself() {
return (Self) this;
}
}
// derived 1
static class Class2<Self extends Class2<Self, T>, T> extends Class1<Self, T> {
}
// derived 2
static class Class3<Self extends Class2<Self, T>, T> extends Class2<Self, T> {
}
// want to stop recursion; want Class4 has only one parameter
static class Class4<T> extends Class3<Class3, T> {
}
public static void main(String[] args) {
Class1<?, Integer> v1 = new Class1<>();
Class2<?, Integer> v2 = new Class2<>();
Class3<?, Integer> v3 = new Class3<>();
System.out.println(v1.toString());
System.out.println(v2.toString());
System.out.println(v3.toString());
}
}
If it is not possible to stop recursion, then why?
If there is no logic reason of being not able to stop it, then what about adding this feature in next versions of Java?
For example, keyword ThisClass can be added or something.
I'm not 100% clear what the issue is, beyond fixing the compilation issue. You can do that by simply changing the definition of Class4 to this:
static class Class4<T> extends Class3<Class4<T>, T>
^^
[Live example]
I want to understand a bit more on java generics covariance and contravariance and how extends and super together work w.r.t collection and individual objects
class P1 {}
class P2 implements Collection<P2> { // implement collection methods }
class Base<T> {
transient List<T> _instList = new ArrayList<>();
protected Base(final List<T> list) {
_instList = list;
}
}
#SuppressWarnings("unchecked")
class Emp<T> extends Base<Object> {
public <C extends Collection<T>> Emp(final List<? super C> list) {
super((List<Object>) list);
}
public <C extends Collection<T>> void salary(C c) {
}
public <C extends Collection<? super T>> void credit(C c) {
}
}
In Main method,
1 Emp<P1> emp1 = new Emp<>(new ArrayList<P1>());// works, compiles fine
2 Emp<P2> emp2 = new Emp<>(new ArrayList<P2>());// works, compiles fine
3 emp1.credit(new P1()); // doesn't compile
4 emp2.credit(new P2()); // works
I want to understand how & why line 1 works when P1 is not a type of Collection
2 & 4 does compile, because P2 is a sub type of Collection.
Please clarify
Line 1 matches the typebound of the constructor.
class Emp<P1> extends Base<Object> {
public <C extends Collection<P1>> Emp(final List<? super C> list) {
super((List<Object>) list);
}
replacing T with P1 and C is ArrayList.
Nothing in that code requires P1 actually to implement Collection.
It is required to call credit(). Thats why you get an error in line3.
I have some code like this
import com.google.common.collect.Sets;
public void handleInput(Set<Object> conditions){
Set<Set<Object>> powerSet = Sets.powerSet(conditions);
...
}
This works fine. But I want to do this:
public void handleInput(Set<? extends Object> conditions){
Set<Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
so I can get the powerset of objects that are subclasses of object. But this won't compile and I get the error:
Type mismatch: cannot convert from Set<Set<capture#1-of
? extends Object>> to Set<Set<? extends Object>>
How can I achieve this goal?
EDIT: I guess it has something to do with the generic type getting erased at compile time, so that the compiler can't know that powerSet won't add something illegal to the sets it's creating. I've reworked the client, by casting all the inputs to Object, and removing the wildcard altogether. Is this the best way? Thanks!
In this case it doesn't make any sense - since all Java classes extend java.lang.Object at some point.
So ? extends Object is redundant.
But speaking of Sets.powerSet, this works like a charm:
public class TestClass {
public static class A {}
public static class B extends A {}
public static class C extends B {}
public Set<? extends Set<? extends A>> exampleMethod(Set<? extends A> input) {
return Sets.powerSet(input);
}
public static void main(String[] args) {
final TestClass testClass = new TestClass();
final A a = new A();
final B b = new B();
final C c = new C();
System.out.println(
testClass.exampleMethod(
ImmutableSet.of(a, b, c)
)
);
}
}
as #slnowak notes, when you are extending Object, the code is really redundant.
However, to understand the Exception and avoid it...
public void handleInput(Set<? extends Object> conditions){
Set<? extends Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
this will compile and, more usefully, you can restrict the types in your conditions argument using this method, for instance - you could have:
public void handleInput(Set<? extends Number> conditions){
Set<? extends Set<? extends Number>> powerSet = Sets.powerSet(conditions);
...
}
and this would prevent you passing in sets that had non-numeric types and warn you of this at compile time.
I have an interesting discrepancy between javac and Eclipse IDE compiler, and can't figure out who's right. So, the code below compiles with javac, however Eclipse tells me that the static initializer's invocation of "exportAll" is wrong, 'cause:
The method exportAll(Iterable< X.Stat< ? extends Number>>) in the type X is not applicable for the arguments (Collection< capture#1-of ? extends X.Stat>)
Who's right? javac or Eclipse?
import java.util.Map;
public class X {
interface Stat<T> {
}
public static void exportAll(Iterable<Stat<? extends Number>> vars) {
}
public static Map<Double, ? extends Stat> getPercentiles() {
return null;
}
static {
exportAll(getPercentiles().values());
}
}
You can't compile your example - you are calling a non-static method getPercentiles from the static initializer, so I'll assume that it is a static method, too.
In any case, your compiler would at least spit out an "unchecked" warning if you compile with -XLint:unchecked (Stat takes a type parameter!). I assume you would like the following:
public class X {
interface Stat<T> {
}
public static void exportAll(Iterable<? extends Stat<? extends Number>> vars) {
}
public static Map<Double, ? extends Stat<Double>> getPercentiles() {
return null;
}
static {
exportAll(getPercentiles().values());
}
I assume that your percentiles are an arbitrary subclass of Stat<Double>, therefore I declared them as ? extends Stat<Double> in the Map. So the values() call will return a Collection<? extends Stat<Double>>.
Collection implements Iterable, therefore we are safe on that side.But Collection<? extends Stat<Double>> is not covered by Iterable<Stat<? extends Number>>, therefore we need to declare the argument as Iterable<? extends Stat<? extends Number>>.
The beauty (well, except for the syntax) of having exportAll take Iterable<? extends Stat<? extends Number>> is that your Map could contain all sorts of ? extends Stats<N> where N is a subclass of Number.
Your Map<Double, ? extends Stat>.values() will have a type of Collection<? extends Stat>. This Stat is really Stat<?>. Your Iterable requires that the Stat not just be any old Stat<?> but rather Stat<? extends Number>. You would have to change your getPercentiles() to be Map<Double, ? extends Stat<? extends Number>>.
public static void exportAll(Iterable<Stat<? extends Number>> vars) {
} // ^ this must match
// your values type on the map
public Map<Double, ? extends Stat<? extends Number>> getPercentiles() {
return null;
}
#emboss here's what I would do in those cases whenever I could:
class Main {
static interface Something<E> {
void doSomething();
}
static class ConcreteSomething<E> implements Something<E> {
E data;
ConcreteSomething(E data) {
this.data = data;
}
public void doSomething() {
System.out.println(data);
}
}
public static void main(String[] args) {
List<Something<Number>> list = new LinkedList<Something<Number>>();
list.add(new ConcreteSomething<Number>(Math.PI)); // an autoboxed Double
list.add(new ConcreteSomething<Number>(new Integer(5))); // an Integer
for(Something<Number> s : list) s.doSomething();
}
}
It doesn't compile in javac. (after adding static to getPercentiles)
Get your facts straight; don't waste other people's time.
Kids today, too much ADD.