Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics - Don't allow subclasses

I have a question about generics. So lets say I have 3 classes "Foo", "Bar" (which is a subclass of Foo), a class "Factory" and an interface "TestInterface". I want to write a generic method, that only allows objects which "directly" implement the interface, so no subclasses. It is pretty hard for me, since Java8 seems to be "smarter" than Java7.

So heres an example:

public interface TestInterface<T extends TestInterface<T>> {

}

public class Foo implements TestInterface<Foo>{

}

public class Bar extends Foo {

}

public class Factory {

   public static <T extends TestInterface<T>> T doSth(T arg) {
       return arg;
   }
}

Okay, these are my classes. Now in the doSth(T arg) method I want to allow Foo-objects in this case, since it's the only class which "directly" implements the Interface.

Obviously sth like this won't compile now:

Bar b = Factory.doSth(new Bar());

since Bar doesnt implement TestInterface<Bar>.

Okay my problem now is, that this line will compile without any issues:

Foo f = Factory.doSth(new Bar());

Even tho the generic parameter is T and T should implement the interface with its own class (sorry, dont really know how to say it, hope you know what I mean), the compiler accepts "Bar" as a parameter.

So the compiler must behave like "okay Bar doesn't fit, but maybe a superclass would fit." And then it "sees" that it works with the superlcass "Foo". And then the Bar will be treated like a Foo, since every Bar is some kind of a Foo or sth like that?? At least that's how I imagine it.

Well, if I try to compile the line with Java7, it fails. So it seems like Java8 is a little bit smarter with the type-erasure?!

Is there any way in Java8 to not allow subclasses, so that this won't compile?:

Foo f = Factory.doSth(new Bar());

Hope you understand my problem, I'm rarely writing in English.

Greets

like image 309
FelRPI Avatar asked Sep 29 '22 12:09

FelRPI


1 Answers

The reason why Foo f = Factory.doSth(new Bar()); works in Java 8 and not Java 7 is because Java 8 has better type inference. In Java 7 the type is inferred to:

Foo f = Factory.<Bar>doSth(new Bar());

Bar doesn't implement TestInterface<Bar>, but it does implement TestInterface<Foo> by inheritance. Java 8's type inference got a boost, and the type will be inferred to:

Foo f = Factory.<Foo>doSth(new Bar());

Note that Factory.<Foo>doSth() expects a Foo as argument so new Bar() is implicitly cast to Foo which works fine.

I'm afraid there's no way around this. You will always be able to upcast an object to its super class. You could make Foo final, but that disallows subclassing of Foo entirely.

like image 152
ReyCharles Avatar answered Oct 06 '22 20:10

ReyCharles