I have a tricky problem with Java generics, or maybe I can't see the wood for the trees.
I have three classes, A, B, and C that look as follows.
abstract class A<T extends A<T>> {
abstract T sefl();
};
abstract class B<T extends B<T>> extends A<T> {
};
class C extends B<C> {
#Override
C sefl() {
return this;
}
}
Later I have different versions of B's as well as different versions of C's. Furthermore, I have a function test that should accept a list of B's (or of one of its cousins). Or in general, it should accept any list of elements that inherit from A. Unfortunately, I need to know the type of the list's elements in the function's body, i.e. the uppermost type a.self can return (or the type T). Function test looks as follows:
static <T extends A<T>> void test(List<T> list) {
for (A<T> a : list) {
#SuppressWarnings("unused")
T t = a.sefl();
}
}
Now, calling test with a list of C's works.
List<C> cs = new LinkedList<C>();
test(cs);
But
List<B> bs = new LinkedList<B>();
test(bs);
results in a warning requiring a type parameter and
List<B<?>> bs = new LinkedList<B<?>>();
test(bs);
is not valid. Where is my mistake, or how can I create a list of B's that is accepted by function test?
Some words to the motivation behind this problem. The classes A, B, and C (or Animal, Mammal, and Cat) implement a tree-like data structure, where each class extends the structure with some properties. Typically, all super-classes are abstract and you can only create instances from leaf-classes, e.g. cat. Now, the difficulty is that the classes implement a copy-on-write policy (COW), i.e. modifying an object creates and returns a new instance of itself with the modified property.
For example, let’s say all animals have an age property. You can easily define this property in Animal, and you can provide a getter method to return the age.
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
};
However, how do you define the setter method? You can write it as follows:
public abstract Animal setAge();
This requires that (at least) each non-abstract element must implement the setter function. For example:
class Cat extends Mammal<C> {
#Override
public Animal setAge(int age) {
return new Cat(/* .. */);
}
}
Remember, as we implement a COW policy, we must create a new instance. So, in the setter function (e.g. implement in Cat) we return a new cat with the new age. Calling cat.setAge(4) on a Cat element returns a new Cat. Unfortunately, because of the type signature, we only now that we got an Animal returned from setAge, even if we call it on a Cat directly. The twist with the generics helps to reveal the concrete type when calling setAge. So, we can construct Animal like this:
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
public abstract T setAge();
};
And in Cat we can say:
class Cat extends Mammal<C> {
#Override
public Cat setAge(int age) {
return new Cat(/* .. */);
}
}
So, back to the problem. Your right, using List<? extends Animal<?>> as the type of the list works, but unfortunately, I need some way to know the type of the elements. Or more concrete: Function test must replace the old element with the new one. For example:
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
#SuppressWarnings("unused")
Animal<?> a = animal.setAge(4711);
list.add(a);
}
}
And unfortunately, the list extension list.add(a); ist the statement that doesn't work with this signature.
Well, they are two very different implementations:
class C ...
and
class B<T extends B<T>> ...
The class C doesn't declare any generic type.
Simple letters for class names are a bit confusing here, so let's do:
abstract class Animal<T extends Animal<T>> {
abstract T sefl();
};
abstract class Mammal<T extends Mammal<T>> extends Animal<T> {
};
class Cat extends Mammal<Cat> {
#Override
Cat sefl() {
return this;
}
}
So:
List<Cat> catList = new LinkedList<>();
works well, as there is no generic type involved. The compiler determines that
Cat extends Mammal<Cat> ( == Cat extends Animal<Cat> )
fits within the bounds <T extends Animal<T>>
On the other hand for
List<Mammal> mammalList = new LinkedList<>();
test(mammalList); // ok, but mammal list of what???
the compiler can't match the bounded types.
In fact, Mammal<T extends Mammal<T>> extends Animal<T> doesn't have anything to do with <T extends Animal<T>>.
Even by providing a wildcard, you'll never be able to pass a List<Mammal<?> to test. The method signature rejects it!
A possible solution:
A more generic test method
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
Animal a = animal.sefl();
}
}
can be used along with different List types:
List<? extends Mammal<?>> bs = new LinkedList<>();
test2(bs);
List<Cat> catList = new LinkedList<>();
test2(catList);
List<Animal<Cat>> animalList = new LinkedList<>();
test2(animalList);
Java version:
java 9.0.4
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
Related
I'm currently working on inheritance in Java. I would like to discuss the following case with you.
In my example, I have numerous animal and enclosure classes. All animals are derived from BaseAnimal. All enclosure are derived from BaseEnclosure. Both base classes provide various concrete methods - but also some abstract methods.
Now, when implementing a CatEnclosure, I want to specify that when CatEnclosure.resettleTo(Enclosure) is called, only one cat enclosure can be passed. In my current code, a cat could also be placed with a dog.
To my understanding, I would have to define the class of the future (derived) class when creating the abstract method resettleTo in the BaseEnclosure class.
My idea was to use a second generic. So BaseEnclosure<A> becomes BaseEnclosure<E, A>. But now I would also have to specify that E must be derived from BaseEnclosure. In addition, of course, A should also be of the BaseAnimal type.
So I get: BaseEnclosure<E extends BaseEnclosure, A extends BaseAnimal>
My IDE now complains that BaseEnclosure and BaseAnimal are raw types. If I write BaseEnclosure<E extends BaseEnclosure<?,?>, A extends BaseAnimal<?,?>>, it works. However, I don't know whether all of this makes sense in terms of design.
I look forward to your suggestions.
Enclosed you get the example code.
public abstract class BaseAnimal<E> {
protected E enclosure;
public void setEnclosure(E enclosure) {
this.enclosure = enclosure;
}
public E getEnclosure() {
return enclosure;
}
public abstract String getNoise();
}
public abstract class BaseEnclosure<A> {
protected List<A> animals = new ArrayList<A>();
// some methods...
public List<A> getAnimals() {
return animals;
}
public abstract void resettleTo(BaseEnclosure other);
}
public class Cat extends BaseAnimal<CatEnclosure> {
#Override
public String getNoise() {
return "miiiaaauu";
}
}
public class CatEnclosure extends BaseEnclosure<Cat>{
#Override
public void resettleTo(BaseEnclosure other) {
// hm...
}
}
public class Dog extends BaseAnimal<DogEnclosure> {
#Override
public String getNoise() {
return "wuff";
}
}
public class DogEnclosure extends BaseEnclosure<Dog>{
// some methods...
#Override
public void resettleTo(BaseEnclosure other) {
// hm...
}
}
public class Main {
public static void main(String[] args) {
DogEnclosure doghouse = new DogEnclosure();
Dog dog = new Dog();
// later: JPA
doghouse.getAnimals().add(dog);
dog.setEnclosure(doghouse);
CatEnclosure catbox = new CatEnclosure();
Cat cat = new Cat();
// later: JPA
catbox.getAnimals().add(cat);
cat.setEnclosure(catbox);
// OHOHOH!!!
doghouse.resettleTo(catbox);
}
}
I want to specify that when CatEnclosure.resettleTo(Enclosure) is called, only one Cat Enclosure can be passed. In my current code, a cat could also be placed with a dog.
Assuming here, you dont want CatEnclosure to resettleTo DogEnclosure.
Your scenario is a typical case of circular reference in generics. Based on this post, you need to redefine your base classes as follows:
public abstract class BaseAnimal<A extends BaseAnimal<A, E>, E extends BaseEnclosure<E, A>> {...}
public abstract class BaseEnclosure<E extends BaseEnclosure<E, A>, A extends BaseAnimal<A, E>> {...}
class Dog extends BaseAnimal<Dog, DogEnclosure> {...}
class DogEnclosure extends BaseEnclosure<DogEnclosure, Dog> {...}
// Similarly Cat and CatEnclosure
Now, to prevent cat enclosure resettling to dog enclosure, you need to change the resettleTo method signature as below:
public abstract void resettleTo(BaseEnclosure<E,A> other);
You will not be allowed to compile the below code:
CatEnclosure catEnclosure = new CatEnclosure();
Cat c = new Cat();
c.setEnclosure(catEnclosure);
DogEnclosure dogEnclosure = new DogEnclosure();
Dog d = new Dog();
d.setEnclosure(dogEnclosure);
catEnclosure.resettleTo(dogEnclosure); // Error type mismatch
This is my code:
interface a {}
class b{}
class c extends b implements a{}
class d extends b{}
class e{
public void makeItWork(){
b[] bees = new b[] {new c(), new d()};
for (b bee: bees){
if (bee instanceof a) {
a beeA = (a) bee;
//how to call the method test if object bee conforms the the interface?
test(beeA.getClass(), beeA);
//this goes wrong
}
}
}
public <T extends a> void test(Class<T> classType, T concrete){
}
}
Besides maybe the bad design, I would like to know if it is possible to call the method test on objects who implements the interface a.
your test method doesn't need a generic type parameter.
You can define it as:
public void test(Class<? extends a> classType, a concrete) {
}
P.S. please use capitalized class names.
You can actually get away without using generics at all here:
public void test(a concrete) {
}
I have the following code that give me trouble compiling.
Give me this error
Error:(20, 22) java: incompatible types: com.company.Main.Impl cannot be converted to T
I only need that interface to work in this function, and I don't want to change the function body much.
Why doesn't this work?
and
How could I make this work?
package com.company;
public class Main {
class Impl implements someinterface, anotherinterface{
#Override
public Integer getInteger() {
return 0;
}
}
class BigObject{
public Impl get(){
return new Impl();
}
}
private <T extends someinterface & anotherinterface> Integer myfunc(BigObject bg){
T xy = bg.get(); // this line will not compile????
if (xy.isOK()) // from someinterface
return xy.getInteger(); // from anotherinterface
return null;
}
public static void main(String[] args) {
// write your code here
}
}
It won't compile because in Java, generics are invariant, see related post.
With the following line of code:
<T extends SomeInterface> Integer myfunc(BigObject bg) { ... }
you are saying that T is something that is some kind of SomeInterface, or, more precisely, a certain type that is a subtype of SomeInterface. The compiler complains about T xy = bg.get(), because bg.get() returns a certain subtype of T, but that type may or may not be the same as Impl.
As an analogy, you are saying something like this:
class Cat extends Animal { }
class AnimalObj {
public Cat get() {
return new Cat();
}
}
private <T extends Animal> Integer myfunc(AnimalObj bg) {
T xy = bg.get();
...
}
T could be a Cat, but it could also be a Dog. Who knows. So that's why the compiler complains.
If you don't care about the subtype, you should drop generics, and write this instead:
private Integer myfunc(AnimalObj bg) {
Animal xy = bg.get();
...
}
Since myFunc accepts a BigObject, which is able to deliver a concrete Impl, you could just replace <T extends someinterface & anotherinterface> by Impl.
Read more
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
Generics – The Java Tutorials
Lets change the myfunc a little bit so that it will return the generic type T:
private static <T extends someinterface & anotherinterface> T myfunc(BigObject bg){
return bg.get();
}
Now you can call it like this:
Impl i1 = myfunc( new BigObject() );
and like this:
Impl2 i2 = myfunc( new BigObject() );
But if BigObject.get would have only returned Impl you would have error with second call that expected Impl2.
I am trying to create a generic method for sub classes lists which add a new element no matter which of the sub class I choose.
for that matter I made an example that will be easy to understand.
There is A zoo container of giraffes and zebras lists. zebra and giraffe are both Animals. I needed to create a'mate' method that will be able to get List of homo gene type meaning list of giraffe or zebra(but not both), and the mate method will add another animal of the type to the existing list (without copying) if there are more than 2 animals in the list.
so to solve this I thought to used reflection since I can not initiate a generic type in java(it is not allowed in java- new T() is not allowed).
so I created an instance of the first element, and tried to add it to the list with animals.add(newAnimal);.. however the compiler complains about it with the following error:
Error: java: no suitable method found for add(Animal)
method java.util.Collection.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
method java.util.List.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
I solve it by getting the add method at run-time using again, reflection, and it is working(the line in comment), however I would like to know why the compiler does not allow me to use add for animal, because I could not find the answer myself.
The code:
class Animal implements Comparable<Animal>{
int numLegs;
#Override
public int compareTo(Animal o) {
return this.numLegs-o.numLegs;
}
}
class Giraffe extends Animal{
int neckLength;
}
class Zebra extends Animal{
int numOfStripes;
}
class Zoo{
List<Giraffe> giraffes=new ArrayList<Giraffe>();
List<Zebra> zebras=new ArrayList<Zebra>();
public void printMostAnimals(){
for(Animal a: getMostAnimals()){
System.out.println(a);
}
}
private List<? extends Animal> getMostAnimals() {
if(giraffes.size()>zebras.size())
return giraffes;
return zebras;
}
public void mate(List<? extends Animal>animals) throws IllegalAccessException, InstantiationException {
if(animals.size()>2){
Class<?> firstAnimalInstanceClass=animals.get(0).getClass();
Animal newAnimal=(Animal)firstAnimalInstanceClass.newInstance();
animals.add(newAnimal);
// animals.getClass().getDeclaredMethod("add",Object.class).invoke(animals,newAnimal);
System.out.println("new mate was added");
return;
}
System.out.println("no mate been added");
}
}
class App{
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Zoo z=new Zoo();
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.mate(z.zebras);
System.out.println("new zebra been added?"+(z.zebras.size()>3));
}
}
Thanks,
Because somebody might write:
List<Giraffe> giraffes = new ArrayList<>();
List<? extends Animal> animals = giraffes;
animals.add(new Zebra());
for (Giraffe g : giraffes) { // but there is a Zebra in there!
g.eatLeavesFrom(tallTree); // Zebras can't do that!
}
To have type safety when mating, you could do:
class Animal<M extends Animal<M>> {
M mate;
}
class Giraffe extends Animal<Giraffe> {}
class Zebra extends Animal<Zebra> {}
which allows you to write:
<A extends Animal<A>> void matingSeason(List<A> animals) {
A x = null;
for (A a : animals) {
if (x != null) {
a.mate = x;
x.mate = a;
x = null;
} else {
x = a;
}
}
}
you cannot add anything other than null to List<? extends Animal>.
Ex:
void foo(List<? extends Animal> animals ){
animals.add(new Animal());// compiler error
}
let's assume for a minute it doesn't raise a compiler error and i call foo(new ArrayList<Dog>()), it will add Animal to a List of dogs. clearly that shouldn't be allowed and thus the above code won't compile.
if this didn't raise an error the whole point of generic compile time safety is gone.
You are not allowed to add an Animal "aa" to a List<Giraffe> because "aa" could be a Zebra.
When you declare a List<? extends Animal>, you tell the compiler that it is a a List<Giraffe>, a List<Zebra> or a List<Animal>. Since it could be a List<Giraffe>, we fall in the case above, which means that you cannot add an Animal.
If you remove the generic, it should work, because you tell the compiler that there could be anything in the list:
((List)animals).add(newAnimal);
However you loose all type safety.
Instead why not do it as follows:
class AnimalList<AnimalType extends Animal> extends List<AnimalType> {
void mate() {
Class<AnimalType> firstAnimalInstanceClass = animals.get(0).getClass();
AnimalType newAnimal = (AnimalType)firstAnimalInstanceClass.newInstance();
add(newAnimal);
}
}
In your Zoo, you would declare:
AnimalList<Zebra>
AnimalList<Giraffe>
and then call mate without arguments:
AnimalList<? extends Animal> list;
list.mate();
Let's say we have a few test interfaces/classes like this:
abstract class Plant {
public abstract String getName();
}
interface Eatable { }
class Apple extends Plant implements Eatable {
#Override
public String getName() {
return "Apple";
}
}
class Rose extends Plant {
#Override
public String getName() {
return "Rose";
}
}
interface Animal {
<T extends Plant & Eatable> void eat(T plant);
}
You can see Animal.eat is a generic method with constraints. Now I have my Human class like this:
class Human implements Animal {
#Override
public void eat(Plant plant) {
}
}
which compiles fine. You can see Human.eat is less constrained than Animal.eat because the Eatable interface is lost.
Q1: Why doesn't the compiler complain about this inconsistency?
Q2: If Plant&Eatable downgrades to Plant is acceptable for the compiler, why it complains on eat(Object plant)?
Lesson: Generics by Gilad Bracha
according to him
public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll)
This is an example of giving multiple bounds for a type parameter,
using the syntax T1 & T2 ... & Tn. A type variable with multiple
bounds is known to be a subtype of all of the types listed in the
bound. When a multiple bound is used, the first type mentioned in the
bound is used as the erasure of the type variable.
so your example <T extends Plant & Eatable> void eat(T plant); will be erased to void eat(Plant plant); so when you override it the compiler doesn't complain
Ahmed answer is right,by the way,if you wanna put constraint the implementation of the interface of Animal, you should declare it as this:
interface Animal<T extends Plant & Eatable> {
void eat(T plant);
}
Then, if you implements the Animal interface without providing the type information, the compiler will using the least surprise policy to infer the T as a Plant type. But if you provide the necessary type information, the compiler works fine.
class Human implements Animal<Rose> // won't compile
class Human implements Animal<Apple> // compile