I have two classes that extend a common base class. The base class has code that, for one concrete subclass, needs to know the Class of the other concrete subclass. So, given Foo and Bar extending Base, an instance of Foo needs to know Bar.class, and an instance of Bar needs to know Foo.class.
And, silly me, I'm trying to Do the Right Thing and use Java generics to ensure that the subclasses return a valid Java class object, one that extends the base class.
So, I tried this:
class Base {
abstract protected <T extends Base> Class<T> getOtherClass();
}
The compiler seems reasonably happy with that construction. The problem comes in the implementations.
First, I tried:
class Foo extends Base {
#Override
protected <T extends Base> Class<T> getOtherClass() {
return Bar.class;
}
}
(where Bar also extends Base)
That complains that I have a type mismatch in the return value, and it requires a cast.
Then, I tried:
class Foo extends Base {
#Override
protected Class<Bar> getOtherClass() {
return Bar.class;
}
}
Now the compiler complains about needing a cast at Class<Bar>.
Then I tried:
class Foo extends Base {
#Override
protected Class<Base> getOtherClass() {
return Bar.class;
}
}
Now I get both complaints: needing a cast in the Class<Base> and in the return value.
Is there a way of expressing this that avoids any casts?
abstract class Base<T extends Base> {
abstract Class<T> getOtherClass();
}
class Foo extends Base<Bar> {
#Override Class<Bar> getOtherClass() { return Bar.class; }
}
...or...
abstract class Base {
abstract Class<? extends Base> getOtherClass();
}
class Foo extends Base {
#Override Class<Bar> getOtherClass() { return Bar.class; }
}
Related
I have a strange problem, I'm not capable to understand. I guess It's because I'm not familiar with generics.
public abstract class AbstractClass <T extends AbstractClass <T>> {
//...
public <T extends AbstractClass> T genericMethod(){
//do stuff
return (T) this;
}
}
No using this with a non-abstract Class
public class MyClass extends AbstractClass <MyClass> {
//...
public void anotherMethod() {
//do other stuff
}
}
So far more or less ok (maybe not ideal, but ok). Now the confusing part:
private MyClass stubWithMyClassAsReturnValue(){
//more stuff
return new MyClass();
}
public void test1(){
MyCalss myClass = stubWithMyClassAsReturnValue().genericMethod();
myClass.anotherMethod();
//---> works just fine
}
public void test2(){
stubWithMyClassAsReturnValue().genericMethod().anotherMethod();
//---> compiling error because AbstractClass does not know about anotherMethod(),
// so genericMethod() returns AbstractClass and not MyClass
}
I guess there is some kind of implicit casting. How can I make the code more fluent and get rid of this superfluous "myClass"? I'm pretty sure there's an obvisious mistake but I don't see it.
In this code:
// this T
// v
public abstract class AbstractClass <T extends AbstractClass <T>> {
// and this T
// v
public <T extends AbstractClass> T genericMethod(){
//do stuff
return (T) this;
}
}
The two Ts highlighted are different Ts. The first T is the generic parameter of the class AbstractClass. The second T is the generic parameter of the genericMethod. These two Ts are not related, and are independently inferred.
When you do this:
stubWithMyClassAsReturnValue().genericMethod()
The compiler knows what the first T is - MyClass, but it doesn't know what the second T should be, so it just uses the upper bound - AbstractClass. But anotherMethod is not available in AbstractClass, hence the error.
You likely only need one T:
public abstract class AbstractClass <T extends AbstractClass <T>> {
public T genericMethod(){
//do stuff
return (T) this;
}
}
I was implementing some architecture when I saw the following error:
Error:(33, 55) java: incompatible types: inferred type does not conform to upper bound(s)
inferred: java.io.Serializable
upper bound(s): sandbox.ExpirePolicy,java.io.Serializable
The whole simplified code is below:
interface Configuration<K,V>{}
interface ExpirePolicy{}
interface Factory<T>{}
class FactoryBuilder {
public static <T extends Serializable> Factory<T> of(T instance){
System.out.println(instance.getClass());
return new Factory<T>() {};
}
}
class BaseConfiguration<K,V> implements Configuration<K,V> {
public BaseConfiguration<K,V> setExpiryPolicyFactory(Factory<? extends ExpirePolicy> factory){
return this;
}
}
class C<K,V> extends BaseConfiguration<K,V> {
public C<K,V> setExpiration(){
super.setExpiryPolicyFactory(FactoryBuilder.of((Serializable) getExpirePolicy()));
return this;
}
private ExpirePolicy getExpirePolicy(){
return new ExpirePolicy() {};
}
}
The exception is in trying to call setExpiryPolicyFactory(Factory<? extends ExpirePolicy> factory) with instance of Factory<Serializable>
But if i delete generic in extends BaseConfiguration<K,V> the program will be successfully compiled.
So the next declaration of class C is correct:
class C<K,V> extends BaseConfiguration {
public C<K,V> setExpiration(){
super.setExpiryPolicyFactory(FactoryBuilder.of((Serializable) getExpirePolicy()));
return this;
}
private ExpirePolicy getExpirePolicy(){
return new ExpirePolicy() {};
}
}
The question is: why the second implementation(of class C) will be successfully compiled and the first not?
UPD:
Simpler example of question (delete <T> from extends Base<T>) and program compiles well :
class Base<T> {
public void test(ArrayList<? extends CharSequence> list) {}
}
class Derived<T> extends Base<T> {
public void callTest() {
super.test(new ArrayList<Integer>());
}
}
When you delete <T> from extends Base<T> statement, the Base class starts to be treated as a raw type.
According to Java spec:
The supertype of a class may be a raw type. Member accesses for the
class are treated as normal, and member accesses for the supertype are
treated as for raw types. In the constructor of the class, calls to
super are treated as method calls on a raw type.
This means that super.test(...) call is also treated as method call on a raw type as if it has been declared like:
public void test(ArrayList list) {}
Thus no compilation errors happens.
It seems like the factory builder should take in an ExpirePolicy instead of Serializable for creating the factory. Changing the signature to
class FactoryBuilder {
public static <T extends ExpirePolicy> Factory<T> of(T instance){
System.out.println(instance.getClass());
return new Factory<T>() {};
}
}
enables using
class C<K,V> extends BaseConfiguration<K,V> {
public C<K,V> setExpiration(){
super.setExpiryPolicyFactory(FactoryBuilder.of(getExpirePolicy()));
return this;
}
private ExpirePolicy getExpirePolicy(){
return new ExpirePolicy() {};
}
}
without extra casts.
The second implementation of C compiles, but with warnings, because it's using raw types.
Assuming we have a method like this:
public void foo(Class<? extends ClassBar> x) {
...
}
By modifying the generic expression;
< ? extends ClassBar >
Is it possible to ensure that ClassBar.class can't be passed in but anything extends ClassBar directly or indirectly be passed in WITHOUT throwing an exception on the runtime?
If you have only a bunch of classes extending ClassBar you can follow these two approaches.
Solution 1:
have all subclasses of ClassBar extend a custom interface (except for ClassBar itself), and change the method signature to:
public <T extends ClassBar & MyInterface> void foo(Class<T> x) {
...
}
Solution 2:
use something similar to this #AndyTurner's trick and provide instances only for specific types.
E.g:
class ClassBar {}
class ClassBarA extends ClassBar{}
class ClassBarB extends ClassBar{}
Your class containing foo:
class Foo<T extends ClassBar> {
private Foo() {} // private constructor
public static <T extends ClassBarA> Foo<T> instance(T c) {
return new Foo<T>();
}
public static <T extends ClassBarB> Foo<T> instance(T c) {
return new Foo<T>();
}
public void foo(Class<T> c) {
}
}
Only subclass of ClassBarA would be accepted in this case
Foo<ClassBarA> foo1 = Foo.instance(this.classBarA);
foo1.foo(ClassBarA.class); // pass
foo1.foo(ClassBar.class); // fail
public abstract class BaseClass<T extends BaseClass<T>> {
T method1(){
return getThis();
}
public abstract T getThis();
}
public class SubClass extends BaseClass<SubClass> {
public SubClass getThis(){
return this;
}
}
If it's just one level of inheritance i can do something like the above and get a reference of SubClass when I call method1().
What if I have inheritance at two levels like
public abstract class SubClass1<T extends SubClass1<T>> extends BaseClass<SubClass1<T>> {
#Override
public SubClass1<T> getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
}
what should I change to the method1 and BaseClass so that when method1 is called I can get back SubSubClass1 type. I wan't to do this without any suppress warnings
It is important to understand that each type-parameterized class's type parameters are its own. In particular, they (the parameters themselves) are not shared with super- or subclasses. It is common for type-parameterized subclasses to intentionally share type parameter values with their superclasses, but that's a different thing.
Classes get to specify their superclasses, but they do not get to alter their superclass' own inheritance. Thus, if SubClass1 extends BaseClass<SubClass1<T>> then BaseClass's type parameter for class SubClass1<T> and every subclass thereof is SubClass1<T>, because that class alone is the one that SubClass1<T> extends.
Any concrete subclass of your BaseClass<T> must implement its abstract method getThis(), and that requires such a class (e.g. SubClass1) to specify BaseClass's type parameter T as an instantiable type (else it cannot provide a return value). From that point on down the hierarchy, subclasses can employ covariant return types to narrow the type returned by getThis(), but they cannot change the BaseClass type parameter specified by SubClass1, except to the extent that it depends on SubClass1's own type parameter(s). For example, this compiles cleanly:
public abstract class BaseClass<T extends BaseClass<T>> {
// ...
public abstract T getThis();
}
public abstract class SubClass1<T extends SubClass1<T>> extends BaseClass<SubClass1<T>> {
#Override
public SubClass1<T> getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
#Override
public SubSubClass1 getThis() {
return this;
}
}
But that's an exercise of covariant return types, not type parameterization, so this also works:
public abstract class BaseClass {
// ...
public abstract BaseClass getThis();
}
public abstract class SubClass1 extends BaseClass {
#Override
public SubClass1 getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1 {
#Override
public SubSubClass1 getThis() {
return this;
}
}
Props to #HannoBinder who first suggested covariant return types in the comments.
Note that if the main idea here was use a type parameter to mark which descendant of BaseClass you actually have -- or at least an upper bound on that -- then that's wrongheaded. In that approach, the type BaseClass<SomeSubClass> is no more meaningful or expressive than the type SomeSubClass itself. The parameterized version is distinguished only by being harder to work with.
SubSubClass1 must be the one implementing getThis(), like this:
public abstract class BaseClass<T> {
T method1(){
return getThis();
}
public abstract T getThis();
}
public abstract class SubClass1<T> extends BaseClass<T> {
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
#Override
public SubSubClass1 getThis() {
return this;
}
}
Imagine we have following classes:
public interface MyInterface<T> {
List<T> getList(T t);
}
abstract class BaseClass<T extends Number> implements MyInterface<T> {
#Override
public List<T> getList(Number t) {
return null;
}
}
class ChildClass extends BaseClass<Integer> {
#Override
public List<Integer> getList(Integer t) {
return super.getList(t); //this doesn't compile
}
}
getList in ChildClass doesn't compile, the output is:
abstract method getList(T) in com.mypackage.MyInterface cannot be accessed directly
I can't get why BaseClass.getList method isn't overriden in ChildClass.
But what makes me completely confused is the fix that makes it compile:
class ChildClass extends BaseClass<Integer> {
#Override
public List<Integer> getList(Integer t) {
return super.getList((Number) t); //Now it compiles!
}
}
So I cast Integer to Number, and is solves the problem.
Could anyone explain what's going on in this code?
Your base class should look like:
abstract class BaseClass<T extends Number> implements MyInterface<T> {
#Override
public List<T> getList(T t) {
return null;
}
}
You weren't using T, but the Number class as a parameter.
It doesn't override because the abstract method takes a Number as a parameter and the concrete method takes an Integer. They must be the same in order to override.
You should change your abstract class implementation to take type T as a parameter.
Why isn't the superclass method defined as
public List<T> getList(T t)
?
What is going on in the imaginary class.
abstract class BaseClass<T extends Number> implements MyInterface<T> {
#Override
public List<T> getList(Number t) {
return null;
}
}
This class has one generic parameter (T) that has to extend Number class and implement the interface MyInterface
You also Try to override a method that does not exists, because this class do not extend other any class. While a class is implementing an interface there is no need to override the interface method because the are only the description.
What happen if we remove the #override annotation.
abstract class BaseClass<T extends Number> implements MyInterface<T> {
public List<T> getList(Number t) {
return null;
}
}
In this case we do not implement the method from the interface but create a new one, a this method parameter is Number that is same type as T, it will probably cause some error that class has two the same methods. (not tested by compiler)
Them implementation of this method should look like this
abstract class BaseClass<T extends Number> implements MyInterface<T> {
public List<T> getList(T t) { //Because T is allready restricted to be Number
return null;
}
}
And when You specify the type You will not have a problem to call this method when you override it
class ChildClass extends BaseClass<Integer> {
#Override
public List<Integer> getList(Integer t) {
return super.getList(t);
}
}
In advance You don have to implement it only for return null and then override it in some child class. What You can do is create class like this
abstract class BaseClass<T extends Number> implements MyInterface<T> {
private List<T> list = new ArrayList<T>(); //The way of initialization is up to You
public List<T> getList() { //Because T is allready restricted to be Number
return list;
}
}
As other colleagues pointed out the reason for issue is incorrect signature of parent method. The reason why the casting works is due to the way how compiler treats generics. It guarantees that there won't be runtime ClassCastException issues if you use generic but only if you don't do casting. As soon as you did it you actually said compiler to shut up as you know better what your type really is. However after this you potentially could get ClassCastException in runtime (not in this case I assume)