Reputation: 997
The following will not compile but I wish to create something of similar functionality which will compile:
public class FreezerTest
{
interface Edible{}
interface SmallerThanABeachball{}
interface Freezeable{}
abstract class BoxedItem
{}
class Marbles extends BoxedItem
{}
class IceCream extends BoxedItem implements Freezeable, SmallerThanABeachball, Edible
{}
class MyBrother
{}
class Banana implements Edible, SmallerThanABeachball
{}
class Cat implements SmallerThanABeachball
{}
abstract class StorageChest<T>{
public void add(T toStore){}
}
class MiniFoodFreezer extends StoreageChest<Freezeable & Edible & SmallerThanABeachball>{
}
public FreezerTest(){
MiniFoodFreezer freezer = new MiniFoodFreezer();
freezer.add(new Cat());//DESIRE COMPILE ERROR
freezer.add(new IceCream());//DESIRE OK
freezer.add(new MyBrother());///DESIRE COMPILE ERROR
freezer.add(new Banana());//DESIRE COMPILER ERROR
freezer.add(new Marbles());//DESIRE COMPILER ERROR
}
}//end
One thought was to create an all-encompassing interface and then pass that:
interface WillFitInMiniFoodFreezer extends Edible, SmallerThanABeachball, Freezeable{}
class MiniFoodFreezer extends StorageChest<WillFitInMiniFoodFreezer>{
}
...however what if Edible, SmallerThanABeachball, and Freezeable are all from a 3rd party library and other third-party libraries refer to these types, some of which have the interface implementations necessary meet the criteria for WillFitInMiniFoodFreezer but do not explicitly implement WillFitInMiniFoodFreezer?
Upvotes: 1
Views: 2585
Reputation: 333
as JB Nizet say
I'm afraid you'll have to make it a run-time check, or to create an adapter class that wraps IceCream and implements WillFitInMiniFoodFreezer.
this will solve your problem until java solve its generic nonsense
you suppose the following are in some third party library
interface Edible {
}
interface SmallerThanABeachball {
}
interface Freezeable {
}
abstract class BoxedItem {
}
class Marbles extends BoxedItem {
}
class IceCream extends BoxedItem implements Freezeable, SmallerThanABeachball, Edible {
}
class MyBrother {
}
class Banana implements Edible, SmallerThanABeachball {
}
class Cat implements SmallerThanABeachball {
}
abstract class StorageChest<T> {
public void add(T toStore) {
}
}
//third party class that has function require type implementing the three interfaces
class SomeClass{
<T extends Edible & SmallerThanABeachball & Freezeable> void doSomething(T someThing){
System.out.println(someThing);
}
}
you want to make your own storage that accept types implementing the three interfaces Edible, SmallerThanABeachball, Freezeable
OK not beautiful one but a simple work around
create interface that implement this interfaces
and create a class that do nothing but extending the target class and implementing the new combining interface
interface WillFitInMiniFoodFreezer extends Edible, SmallerThanABeachball, Freezeable {
}
class MiniFoodFreezer extends StorageChest<WillFitInMiniFoodFreezer> {
}
//create your one version of iceCream implement the combining interface
class MyIceCream extends IceCream implements WillFitInMiniFoodFreezer {
}
//you can make new types implementing WillFitInMiniFoodFreezer
class Potato implements WillFitInMiniFoodFreezer {
}
class FreezerTest {
public FreezerTest() {
MiniFoodFreezer freezer = new MiniFoodFreezer();
//what you have wished
freezer.add(new Cat());//DESIRE COMPILE ERROR
freezer.add(new MyIceCream());//DESIRE OK
freezer.add(new Potato());//DESIRE OK
freezer.add(new MyBrother());///DESIRE COMPILE ERROR
freezer.add(new Banana());//DESIRE COMPILER ERROR
freezer.add(new Marbles());//DESIRE COMPILER ERROR
//third party function parametrized with the three interfaces
SomeClass thirdPartyClass = new SomeClass();
thirdPartyClass.doSomething(new MyIceCream()); // ^_^ ok
thirdPartyClass.doSomething(new Potato()); // ^_^ ok
thirdPartyClass.doSomething(new Cat());//:( not ok
}
}//end
Upvotes: 0
Reputation: 55233
The issue here is that Freezeable & Edible & SmallerThanABeachball
is not itself a type - the ampersand (&
) can only be used to define multiple upper bounds when declaring a type parameter, for example <T extends Freezeable & Edible & SmallerThanABeachball>
. This language limitation is further discussed here: How to reference a generic return type with multiple bounds
One workaround is to use a combination of composition and a generic add
method:
class Freezer extends StoreageChest<Freezeable> { }
class MiniFoodFreezer {
private final Freezer freezer = new Freezer();
public <T extends Freezeable & Edible & SmallerThanABeachball> void add(
final T toStore
) {
freezer.add(toStore);
}
}
The downside being that MiniFoodFreezer
no longer is-a StoreageChest
of anything, so you lose any direct benefits of inheritance. However, you can expose differently typed views of the same objects as needed. For example, assume StoreageChest<T>
implements Iterable<T>
:
class MiniFoodFreezer {
private final Freezer freezer = new Freezer();
public <T extends Freezeable & Edible & SmallerThanABeachball> void add(
final T toStore
) {
freezer.add(toStore);
}
public Iterable<Freezeable> asFreezables() {
return freezer;
}
public Iterable<Edible> asEdibles() {
// this is okay because add must take an Edible and Iterable is read-only
@SuppressWarnings("unchecked")
final Iterable<Edible> edibles = (Iterable<Edible>)(Iterable<?>)freezer;
return edibles;
}
public Iterable<SmallerThanABeachball> asSmallerThanBeachballs() {
// same reasoning as above
@SuppressWarnings("unchecked")
final Iterable<SmallerThanABeachball> smallerThanBeachballs =
(Iterable<SmallerThanABeachball>)(Iterable<?>)freezer;
return smallerThanBeachballs;
}
}
Then we can do:
final MiniFoodFreezer miniFoodFreezer = new MiniFoodFreezer();
miniFoodFreezer.add(new IceCream());
miniFoodFreezer.add(new SnoCone());
miniFoodFreezer.add(new Slushy());
for (final Freezeable freezable : miniFoodFreezer.asFreezables()) {
// do freezable stuff
}
for (final Edible edible : miniFoodFreezer.asEdibles()) {
// do edible stuff
}
for (
final SmallerThanABeachball smallerThanABeachBall :
miniFoodFreezer.asSmallerThanBeachballs()
) {
// do smaller-than-a-beach-ball stuff
}
Upvotes: 3