1

There's not much to add to my question, basically:

class A {}
interface I {}

// how can I get a Set<> of object of type A that implements I?

I tried a few things <A & I>, <A extends I>, <? super A extends I> and a few other but didn't find anything that works, so I'm wondering if this is possible at all. If it isn't I'm curious about the reasoning behind it.

Thanks

foo
  • 940
  • 2
  • 9
  • 20
  • 1
    Are A and I actual class and interface names, or do they represent generic type parameters? – Eran Aug 01 '14 at 16:24
  • Looks like same issue as here: [Multiple restrictions on generic type based on super and sub classes in java][1] [1]: http://stackoverflow.com/questions/4085557/multiple-restrictions-on-generic-type-based-on-super-and-sub-classes-in-java – John B Aug 01 '14 at 16:30
  • John is right, it looks like I can't have one such collection without making the enclosing class generic too, that's unfortunate :). – foo Aug 01 '14 at 16:58

5 Answers5

2

Java does not support intersection types, it only supports multiple bounds (as in extends A & I) when declaring type parameters. That is, we can not use a notation like A & I to denote the family of types that extend both A and I, but we can declare a type parameter <T extends A & I> to refer to a specific such type.

If the latter is what you want, a type parameter is a great fit. But if your collection should admit unrelated subtypes of A and I, no nice solutions seem to exist. My best idea is a hack like:

class AISetWrapper {
    Set<A> set = new HashSet<>();

    <T extends A & I> Set<T> getSet() {
        return (Set<T>) set; // unchecked cast that only works because generics are not reified
    }
}

which would allow us to write:

class AI1 extends A implements I { }

class AI2 extends A implements I { }

public static void main(String[] args) {
    AISetWrapper aiSet = new AISetWrapper();
    aiSet.get().add(new AI1()); // compiles
    aiSet.get().add(new AI2()); // compiles
    aiSet.get().add(new A()); // does not compile
    aiSet.get().add(new I() {}); // does not compile
}
meriton
  • 68,356
  • 14
  • 108
  • 175
  • Seems that `class AISetWrapper` would avoid the unchecked cast. – John B Aug 01 '14 at 17:36
  • That is what my answer calls the type parameter approach. As I already said in my answer, that approach restricts all elements of the collection to a *specific* common supertype that extends A and I. That is, you could not add instances of AI1 and AI2 to the same collection. – meriton Aug 01 '14 at 17:45
  • My point is that in the `hack` solution, moving the generic declaration to the class level instead of the method level would remove the cast warning. – John B Aug 01 '14 at 18:07
  • I got that. But doing that would change the meaning, as my comment tries to explain ... – meriton Aug 01 '14 at 18:31
1

You'll have to make A implement I:

interface I {}
class A implements I {}

Set<A> setOfA;

Possible is alsp

class SubA extends A implements I { }
Set <SubA> setOfSubA;

Usage of a class A cannot make it change it's behaviour, as would be indicated by its sudden "implmentation" of I. Where should the implementations of the interface methods come from?

laune
  • 31,114
  • 3
  • 29
  • 42
1

I was able to do the following:

public class MyClass<T extends String & Iterable>{
    private Set<T> mySet;
}

And

public <T extends String & Iterable> void myFancyMethod(Set<T> mySet){}

However when I did

private Set<? extends String & Iterable>

I got a compile error of Syntax error on token "&". Seems that you can do the & syntax when declaring a type <T> but not for wildcards <? ...>.

A better discussion of this can be found at: Java Generics Wildcarding With Multiple Classes

Community
  • 1
  • 1
John B
  • 32,493
  • 6
  • 77
  • 98
0

You can write your own class:

public class MySet<E extends A & I> extends HashSet<E> {
    // blank
}

This will simply ensure that any instances of MySet will contain only objects that extend A and implement I.

bcsb1001
  • 2,834
  • 3
  • 24
  • 35
-1
// how can I get a Set<> of object of type A that implements I?

You cannot guarantee both in a single generic statement. You can do something like

public void addToSet(I iInstance) {
   if(iInstance instanceof A){  
   //logic to add to your set
    }
}
Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
  • And where is the `Set>`, please? – laune Aug 01 '14 at 16:29
  • Since you want your object to both extend `A` and implement `I` you can use either as generics. – Aniket Thakur Aug 01 '14 at 16:30
  • But the above does not put compile-time restrictions that the passed object extend A AND implement I, just one or the other. The point here is compile-time checking because as run-time you can do whatever you want via type-erasure. – John B Aug 01 '14 at 16:43
  • question is about getting set of objects which extend A and implemnt I. It mentions nothing about compile time or run time. Yes objects not suiting the criteria(just implementing I) may compile while adding to the set via above mentioned function but they will not actually be added as the inner check will fail. – Aniket Thakur Aug 01 '14 at 18:58