6

I've looked around and so far haven't seen any way of having both a lower and upper bound on the same wildcard type in java. I'm not sure if it is possible and am leaning towards it being a current limitation of wildcards in java.

An example of what I'm looking for is below.

public class A {}
public class B extends A{}
public class C extends B{}
public class D extends C{}

public static void main(String[] args)
{
    List<A> a = Arrays.asList(new A());
    List<B> b = Arrays.asList(new B());
    List<C> c = Arrays.asList(new C());
    List<D> d = Arrays.asList(new D());

    extendsParam(a); // error as A is not assignable to B
    extendsParam(b);
    extendsParam(c); 
    extendsParam(d); // 3 previous ok as they can produce B

    superParam(a);
    superParam(b);
    superParam(c); // 3 previous ok as they can consume C
    superParam(d); // error as C is not assignable to D

    extendsSuperParam(a); // error as A is not assignable to B
    extendsSuperParam(b);
    extendsSuperParam(c); // 2 previous ok as they can consume C and produce B
    extendsSuperParam(d); // error as C is not assignable to D
}

public static void extendsParam(List<? extends B> blist)
{
    B b = blist.get(0);
    blist.add(new C()); // error
}

public static void superParam(List<? super C> clist)
{
    B b = clist.get(0); // error
    clist.add(new C());
}

public static void extendsSuperParam(List<???> bclist)
{
    B b = bclist.get(0); // ok
    bclist.add(new C()); // ok
}

Looking at the WildcardType.java class for the generic type information it looks like it supports defining a type with that information. However, I cannot find a way to create such a type in the language.

Is there something that I'm missing or is this a type that is currently impossible to describe with the java language?

gline9
  • 73
  • 6
  • I have been struggling with java's generics ever since they were introduced, (15 years ago?) I have done immensely complex stuff with them, I still can't say I fully understand them. I have never had the need to do something like what you are trying to do. I do not think there is a way to achieve it. – Mike Nakis Jan 01 '20 at 23:41
  • 2
    That's not what bounded wildcards are for. If you want to be able to retrieve `B` as well as add `C` then use a `List`. Bounded wildcards provide flexibility for those _calling_ the API—see [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/q/2723397/6395627). – Slaw Jan 01 '20 at 23:51
  • 1
    @Slaw That is correct they do provide flexibility for those calling the API. In this case though if I require that they use `List` then they cannot pass in a `List` even though that is a perfectly legal type for them to use. I could always add a method for each possible type between `B` and `C` but this becomes intractable for large class hierarchies or cases where `B` and `C` themselves are generic types. – gline9 Jan 02 '20 at 00:00
  • Single letter class names are not helping. Constraining a type parameter to a finite set of types between two fixed types wouldn't be *that* useful. What may be ore useful (in some circumstances) is having two type parameters: `T extends B, U extends T`. That gives a sandwich, so long as the supplied object was supplied by a `Supplier` or some such. (Anyway, big shout out for composition over subtyping.) – Tom Hawtin - tackline Jan 02 '20 at 00:09
  • It's _not_ perfectly legal to pass a `List` argument for a `List` parameter though. If it were legal, your method would be able to add an object which extends `B` but does not extend `C`, which can lead to the `List` containing elements whose types should be illegal by the generic definition, and all type safety is lost. The limitations of the generic type system may seem arbitrary sometimes, but they maintain the static type safety of Java. Unfortunately, Java only has _use-site variance_ and not _declaration-site variance_, unlike Kotlin (for example). – Slaw Jan 02 '20 at 00:56
  • According to [`WildcardType.java`](https://docs.oracle.com/javase/8/docs/api/javax/lang/model/type/WildcardType.html) _a wildcard may have its upper bound explicitly set by an `extends` clause, its lower bound explicitly set by a `super` clause, or neither (**but not both**)._ – IlyaMuravjov Jan 02 '20 at 01:22
  • _(follow up: I mention Java doesn't have declaration-site variance, but I don't believe that would help you in this case anyway)_ – Slaw Jan 02 '20 at 01:32
  • 1
    @Bananon Just to point out, there are two types with that name: `java.lang.reflect.WildcardType` and `javax.lang.model.type.WildcardType`. – Slaw Jan 02 '20 at 01:34
  • 1
    Maybe, this [link](https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java/52468735#52468735) would help you. – Oleksandr Pyrohov Jan 02 '20 at 09:35

1 Answers1

2

This can't be done according to the Oracle Docs:

Note: You can specify an upper bound for a wildcard, or you can specify a lower bound, but you cannot specify both.

djk
  • 36
  • 3