I'm trying to figure out why this code won't compile.
I have interface A extended by interface B.
Class C which implements interface B.
When I call a method that takes in a single object of type A, I can pass in an object of type C and it's fine.
When I call a method that takes in a java.util.List of type A, I cannot pass in a java.util.List of objects of type C. Eclipse generates the following error:
The method addAList(List) in the type Test1 is not applicable for the arguments (List)
Source code example is below.
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public void addASingle(A a) {
return;
}
public void addAList(List<A> aList) {
return;
}
// **********************************
public static void main(String[] args) {
Test1 t = new Test1();
C c1 = new C();
List<C> cList = new ArrayList<C>();
cList.add(c1);
t.addASingle(c1); // allowed
t.addAList(cList); // The method addAList(List<Test1.A>)
// in the type Test1 is not applicable for the arguments (List<Test1.C>)
}
// **********************************
public static interface A {
}
public static interface B extends A {
}
public static class C implements B {
}
}
A List<Car> is not a List<Vehicle>. If it was, you could do the following:
List<Car> cars = new ArrayList<>();
List<Vehicle> vehicles = cars;
vehicles.add(new Bicycle());
and you would end up with a list of cars which contains a bicycle. It would ruin the type-safety of generic collections.
You probably should used a List<? extends A> instead of List<A>. List<? extends A> means: a List<some class which is A or which extends A>.
It expects List and you are passing List,
Change it to
public void addAList(List<? extends A> aList) {
return;
}
it expects List of type A....write it in your method signature.
public void addAList(List<? extends A> aList) {
return;
}
by writing this you declare that..your method expects any List which contains any subtype of A...This is called wildcard.
Related
explain my problem:
I have a super abstract class called First and then I have a lot of class that inherit from it.
I want to build a method that I "say" to it "create a ArrayList of one of the types that inherit from First class", but I'm not to able to find solution.
For example:
public abstract class First{
public First(){
}
}
public class FirstOne extends First{
...........
}
//It's a pseudo-code
public class MyProgramClass{
public creatingMethod(TypeThatInheritFromFirstClass x ){
return ArrayList<TypeThatInheritFromFirstClass>;
}
}
I insert creatingMethod in program class,but it can be anywhere(I prefer in First class like static method, but it's an example)
Thank for your time
You could use a type token:
public class SomeGenerics {
public static void main(String[] args) {
List<SubFirst1> list1 = creatingMethod(SubFirst1.class);
List<SubFirst2> list2 = creatingMethod(SubFirst2.class);
}
public static <T extends First> List<T> creatingMethod(Class<T> type) {
return new ArrayList<>();
}
}
class First {}
class SubFirst1 extends First {}
class SubFirst2 extends First {}
EDIT as per the comment:
As you already have the type token, you can use it for creating instances of that type. A little restriction is, that you must know what constructor to use. If they - for example - all have a parameterless constructor, you can create instances like that:
public static <T extends First> List<T> creatingMethod(Class<T> type) throws ReflectiveOperationException {
List<T> result = new ArrayList<>();
result.add(type.newInstance());
return result;
}
If you have a constructor with parameters (again: all sub classes must have the same parameterized constructor), you must go a more difficult way. Example with a string parameter:
public static <T extends First> List<T> creatingMethod(Class<T> type, String param) throws ReflectiveOperationException {
List<T> result = new ArrayList<>();
Constructor<T> constructor = type.getDeclaredConstructor(String.class);
result.add(constructor.newInstance(param));
return result;
}
Consider this hypothetical class (which I found in a online video):
public class Contrived<T extends Number> extends ArrayList<T> {
List <? extends T> values;
......
}
Here the type variables that Contrived can accept is Number or some sub-type of Number. But the class itself inherits from ArrayList, so what-ever type the class gets, will determine the type of ArrayList.
Now there is a field called values, which is List<? extends T values>. so what does this mean here?
Does the list can hold anything that extends T (which in turn extend Number).
by PECS (producer extends, consumer super) rule, can I only use this List to read elements but not add to the list.
how will the constructor look like, if I need to pass a list of doubles or some type T?
You can have a constructor that takes a List<T> or a List<? extends T>. Continuing to contrive classes:
class B extends Number{
public double doubleValue() { return 0; }
public float floatValue() { return 0; }
public long longValue() { return 0; }
public int intValue() { return 0; }
}
class C extends B{}
I add a contrived, legal constructor to the Contrived class, taking a List<? extends T>:
public Contrived(List<? extends T> values)
{
this.values = values;
}
This allows me to write a main method such as the following:
public static void main(String[] args)
{
List<C> bList = Arrays.asList(new C(), new C(), new C());
Contrived<B> ci = new Contrived<B>(bList);
}
I can pass a List<C> into the constructor for a Contrived<B>.
Another design for the contrived constructor that is legal for Contrived could be, taking a List<T>:
public Contrived(List<T> values)
{
this.values = values;
}
Such a constructor doesn't allow a List<C> for a Contrived<B>, because now the types must match, as in the following two Contrived examples:
public static void main(String[] args)
{
List<C> cList = Arrays.asList(new C(), new C(), new C());
Contrived<C> ci = new Contrived<C>(cList); // must match now
}
And
public static void main(String[] args)
{
List<B> bList = Arrays.asList(new B(), new B(), new B());
Contrived<B> ci = new Contrived<B>(bList); // must match now
}
I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
How can I use both types (Base and Subclass) in one list?
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Sub> list );
public void single( Sub p);
}
class C implements I {
public void list( List<Sub> list) { }
public void single( Sub p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
c.list( new ArrayList<Base>() ); // The method list(List<Generics.Sub>) in the type Generics.C is not applicable for the arguments (ArrayList<Generics.Base>)
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Change:
public void list(List<Sub> list);
to:
public void list(List<? extends Base> list);
Using just List<Base> will give you compiler errors like this one:
public static void main(String[] args) {
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(subs); // The method doSomethingWith(List<Base>) in the type Main is not applicable for the arguments (List<Sub>)
}
private static void doSomethingWith(List<Base> bases) {
// Do something with bases
}
If all you're going to pass is List<Base> to doSomethingWith, then this point is moot, since this won't give you a compiler error. If you want to pass lists that are of a specific type (such as List<Sub> above), then you need to change doSomethingWith to:
private static void doSomethingWith(List<? extends Base> bases) {
This fixes the problem. You could also do it at the caller lever (but it's a bit messier):
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(new ArrayList<Base>(subs));
One issue with the wildcard (?) approach is that you can't add new items to the list. To do that, you need something like:
private static <B extends Base> void doSomethingWith(List<B> bases) {
And then add only B instances to bases.
Just declare all your lists as
List<Base> list;
Then you can add both Base objects and objects of any subclass.
Below are the 2 ways to do it....
public void inTake(List<? extends Base> list){
}
Or
public T extends Base void inTake(List<T> list){
}
I asked a similiar question 10 minutes ago, but pasted the wrong code snippet. I'm really sorry about that.
I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
The error message: "The method list(List<Generics.Base>) in the type Generics.C is not applicable for the arguments (List<Generics.Sub>)"
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Base> list );
public void single( Base list );
}
class C implements I {
public void list( List<Base> b) { }
public void single( Base p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
List<Sub> b = new ArrayList<Sub>();
c.list( b ); // error message as above
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Is there any other way but declaring the list-methods argument as type <? extends Base> ?
Below are the 2 ways to do it....
public void list(List<? extends Base> list){
}
Or
public <T extends Base> void list(List<T> list){
}
is there a way to add the animal to the cage? :)
public class Cage<T> extends ArrayList<T> {
public final String name = "foo";
}
public Zoo {
List<? extends Cage<?>> zooCages= new ArrayList<Cage<?>>();
public <T> void addAnimal(String name, T animal){
for(Cage<?> c : zooCages)
if(c.name.equals(name)){
c.add(animal); //compile error
return;
}
}
}
EDIT: Typos.
EDIT 2 : complete the example
Your problem is with the use of unbounded wildcards. See the following example:
Cage<?> cage1 = new Cage<Integer>();
Cage<?> cage2 = new Cage<String>();
Cage<?> cage3 = new Cage<T>();
someMethod(cage1);
someMethod(cage2);
someMethod(cage3);
public void someMethod(Cage<?> cage) {
cage.add(1); // Will not compile
cage.add("A String"); // Will not compile
cage.add(someValueOfTypeT); // Will not compile
}
Technically, the method wasn't strictly necessary, but you can easily see the problem with this code. A Cage<?> can be a Cage<String> or a Cage<Anything>. You can't add anything to it because it could be any specific Cage.
If you want to add T to a Cage it has to be a Cage<T> or a Cage<? super T>. One way you can do this is:
public class Zoo<T> {
private List<? extends Cage<? super T>> cages = new ArrayList<>();
public void addAnimal(String cageName, T animal) {
for(Cage<? super T> cage : cages) {
// Now you can add animal to a cage
}
}
}
(I'm assuming Cage is a collection.)
You have a conceptual error here.
c is declared in the foreach loop as a Cage<?>, i.e. a cage that accepts an unspecified type. You are trying to add an object of type T to it, but there's no reason to believe that c can contain objects of type T.
See http://download.oracle.com/javase/tutorial/java/generics/wildcards.html for details on what the wildcard * means.
You probably wanted all the cages in the class to contain the same (arbitrary) type T. In this case, the entire enclosing class should take a type parameter. For example:
import java.util.*;
public class Main<T> {
private class Cage extends ArrayList<T> {
public final String name = "foo";
}
List<? extends Cage> zooCages = new ArrayList<Cage>();
public void addAnimal(String name, T animal) {
for(Cage c : zooCages) {
if(c.name.equals(name)) {
c.add(animal);
return;
}
}
}
}