I have created a linked list, but I can't seem to understand how to insert different objects into the same list. Let's say I have a list which I want to contain both objects of type cat and dog, is this possible? I know how to insert only cats, or only dogs, but I can't seem to understand how to insert both into the same list.
You want to use their "lowest" common class, or a common interface, so you can tell as much as possible (and what you need) about list's elements.
If both classes Dog and Cat extend the class Animal, and you want to represent a list of animals, you can do:
List<Animal> animals = new YourList<Animal>();
for (Animal animal: animals){
// use some Animal method
animal.eat();
}
If both of them implement the interface TailOwner and you want to use operations from that interface on members of the list, you can do:
List<TailOwner> tailOwners = new YourList<TailOwner>();
for (TailOwner tailOwner: tailOwners){
// use some TailOwner method
tailOwner.wiggle();
}
If you're not sure, you can always fall back to Object, as all Java classes extend it:
List<Object> objects = new YourList<Object>();
for (Object object: objects){
// use some Object method
System.out.println(tailOwner.toString());
}
Usually, when you have two objects in the same list, they do have something in common.
Either way, you're giving up any Cat-specific or Dog-specific methods (before casting back to either) when you put them in the list.
Having a good hierarchy can prevent casting, and promote polymorphism, when you use objects from the list. All you can tell about objects retrieved from the list at compile time, is that they at least comply to the list's generic type.
If Cat and Dog have nothing in common, you can parameterize the LinkedList with Object:
List<Object> list = new LinkedList<Object>();
This way you will be able to add both Cat and Dog, but please note that this may force you to check the types every time you retrieve the objects from the list.
You only can add elements that implements same interface or extends same class.
class Animal {}
class Cat extends Animal{}
class Dog extends Animal{}
List<Animal> animals = new LinkedList<Animal>;
animals.add(new Cat());
animals.add(new Dog());
OR
interface Animal {}
class Cat implements Animal{}
class Dog implements Animal{}
List<Animal> animals = new LinkedList<Animal>;
animals.add(new Cat());
animals.add(new Dog());
Since your list probably contains elements that are somehow related, it would be best to make an interface and use that as list object type.
Derive both Cat and Dog from IAnimal and make the list of type
List<IAnimal> animals = new YourList<IAnimal>();
I would be cautious when using the word generic, especially since cat and dog are not generic.
LinkedList<Object> linkedlist = new LinkedList<Object>();
linkedList.add(new Dog());
linkedList.add(new Cat());
Related
I know that you use <? extends> wildcard when you only get values out of a collection.
Suppose there's Animal superclass and Dog and Cat subclasses. Basically I want to have a list that contains dogs and cats. I found out I can do the following:
List<? extends Animal> animalList;
List<Dog> dogList = getDogs();
List<Cat> catList = getCats();
animalList = Stream.concat(dogList.stream(), catList.stream()).collect(Collectors.toList())
// Do things according to its type
for (Animal animal : animalList) {
if (animal instance of Dog) {...}
if (animal instance of Cat) {...}
}
The above code compiles. So does it violate the rule in the sense that I'm writing values to animalList?
Stream.concat(dogList.stream(), catList.stream()).collect(Collectors.toList())
This creates a List<Animal>. Crucially, not a List<? extends Animal>. Try it:
List<Animal> = ... all that ...
works fine.
List<Animal> doesn't mean that everything in it was made using literally new Animal(). You can have a List<Animal> that contains solely Cat instances. Those are all animals, that's fine.
The 'point' of ? extends and all that is when you deal with the lists themselves, not with the things within them. Here's specifically why:
List<Animal> x = new ArrayList<Cat>();
x.add(new Dog());
Cat z = x.get(0);
Triple check the above code but it explains exactly why ? extends (and ? super) exists. Generics must be invariant, anything else leads to broken code. The above code must not compile because it makes no sense. As written, it indeed doesn't compile - line 1 isn't allowed. You can 'make it work' by writing List<? extends Animal> x = new ArrayList<Cat>() which compiles fine, but now x.add(new Dog() won't.
The difference is this:
a List<Animal> variable is pointing at some list that is actually some list of specifically <Animal> and not some subtype or supertype of Animal. It might be a LinkedList<Animal> or an ArrayList<Animal>, that's fine, but not an ArrayList<Cat>. With that known, when you 'read' from it, you get Animal objects out, and when you write to it, Animal is fine.
a List<? extends Animal> variable on the other hand is some list that is of Animal or some subtype of Animal. It could be a LinkedList<Dog>. Given that fact, when you read, Animal is fine (Animal f = thatList.get(0) compiles fine), but you can't write anything to it. It might be a list of Dogs, but it could also be a list of Cats, and absolutely no object is therefore save (Except, trivially, literally the expression null, written out like that: thatList.add(null) compiles. And isn't useful, of course).
You assign your List<Animal> expression to a variable of type List<? extends Animal> which is fine. And needless; List<Animal> x = Stream.concat.... would have worked just as well.
Found a difference between collect(Collectors.toList()) and Stream.toList(). See
class Animal { }
class Cat extends Animal { }
record House(Cat cat) { }
class Stuff {
public static void function() {
List<House> houses = new ArrayList<>();
List<Animal> animals1 =
houses.stream()
.map(House::cat)
.collect(Collectors.toList()); // ok
List<Animal> animals2 =
houses.stream()
.map(House::cat).toList(); // compile error
List<Animal> animals3 =
houses.stream()
.map(House::cat)
.map(cat -> (Animal) cat).toList(); // ok
}
}
The collect(Collectors.toList()) is able to give me a List of Animal or List of Cat. But the Stream.toList() can only give a List of Cat.
The question is there any way to make Stream.toList() work. In my real world example I have a class which overrides shutdownNow, which returns a list of Runnable, so my class was calling something.stream().collect(Collectors.toList()), but something.stream().toList() returns a list of MyRunnable.
A part of me wishes they declared the function as default <U super T> List<U> toList() instead of default List<T> toList(), though strangely that is a compile error on my machine (my compiler seems to be ok with U extends T, not U super T).
There is a simple answer here.
Start with
var stream = houses.stream().map(House::cat)
Here, stream has type Stream<Cat>. The Stream::toList method gives you a list of the stream element type, which here is Cat. So stream.toList() is of type List<Cat>. There's no choice here.
Collector has multiple type variables, including the type of the input element, and the type of the output result. There is a lot of flexibility to create a Collector that takes in Cat and produces List<Cat>, List<Animal>, Set<Animal>, etc. This flexibility is partially hidden by the genericity of Stream::collect (and also the generic method Collectors::toList); inferring the generic type parameters for this method can take into account the desired result type on the LHS. So the language papers over the gap between Cat and Animal for you, because there's another level of indirection between the stream and the result.
As #Eugene pointed out, you could get a more general type out:
List<? extends Animal> animals2 = houses.stream().map(House::cat).toList()
This has nothing to do with streams; it is just because List<? extends Animal> is a supertype of List<Cat>. But there's an easier way. If you want a List<Animal>, and you want to use toList(), change the stream type to a Stream<Animal>:
List<Animal> animals = houses.stream().map(h -> (Animal) h.cat()).toList()
Stream::map is also generic, so by having the RHS of the lambda be Animal, not Cat, you get a Stream<Animal> out, and then toList() gives you a List<Animal>.
You could also break it up, if you like that better:
List<Animal> animals = houses.stream().map(House::cat)
.map(c -> (Animal) c).toList()
What is tripping you up is that, because collect is a generic method (and so Collectors::toList), there is additional flexibility to infer a slightly different type, whereas in the simpler stream, everything is more explicit, so if you want to adjust the types, you have to do that in imperative code.
That is impossible to achieve.
Collectors::toList has a declaration of ? extends T. On the other hand Stream::toList returns a List<T>, you are stuck.
You can workaround that (partially), via:
List<? extends Animal> animals2 = houses.stream().map(House::cat).toList();
What you are thinking is U super T, but that is not supported, unfortunately. People occasionally found good real use cases for it - but support is not there.
this a wildcard question.
my purpose is to make a list that contains a class that has a generic type with extended examples:
so this is the structure:
public class Event<T extends ActionType>{
}
public abstract class ActionType{
}
//**many** classes that extends ActionType class
that's a list that hold classes that extends from ActionType
private List<Event<ActionType>> list = ArrayList<>();
Event<RightClick> event = new Event<>(xPosition, yPosition, delay, new RightClick(robot), clicks, robot);
list.add(event);
I know that's I cant do this to make sure that I can add the extended items:
private List<Event<ActionType>> list = ArrayList<>();
but what I can do to add the items to the same list.
to fix my problem I used the wildcard selector ?
List<Event<? extends ActionType>>
Explanation
Generics are invariant. A List<Event<ActionType>> will not accept Event<RightClick>, only Event<ActionType>.
Understand generics and adjust your generic type restrictions.
If generics would be covariant, then you could give someone who expects a List<Animal> a List<Dog> and then add Cats to it. Which would cause heap corruption as dogs.get(0) could suddenly be a Cat.
An example:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
List<Animal> animals = dogs; // pretend this would work
animals.add(new Cat()); // would be legit
Dog dog = dogs.get(1); // should be safe, but is cat, heap corruption
A List<Animal> explicitly says that this list must accept all animals, also cats. But a List<Dog> is restricted to dogs only. The two lists behave differently, they have different restrictions, hence you can not use one for another. Unlike a Dog who is an Animal, a List<Dog> is not an List<Animal>, that is what is meant by co- and invariance.
Solution
The correct tool to "accept any ActionType, I do not care" are wildcards. So either go with
List<Event<? extends ActionType>>
or just
List<Event<?>>
since the Event class already specifies the T extends ActionType restriction.
With that type you will be able to add all sorts of Events to it:
Event<RightClick> rightClick = ...
Event<LeftClick> leftClick = ...
Event<MiddleClick> middleClick = ...
list.add(rightClick);
list.add(leftClick);
list.add(middleClick);
As a consequence of the ? wildcard you will not be able to know the actual type at compile-time anymore, so:
Event<?> event = list.get(0); // unknown which exact type
All you know of ? is that it is at least extends ActionType, so you will be able to use all sorts of methods that are given by ActionType, but nothing introduced only in RightClick for example. That would require an explicit cast (guarded by an instanceof check), although I would question your design if you have to use right-click specific things there.
im a beginner in java and i study everyday from books . there is a quistion that press my brain so hard for so long , what is usefulness of creating an object and give it an another object refrences ! i saw too many examples of this format .
Cat simon = new Cat();
Animal tiger = simon;
The main purpose of an Object reference by itself is to tell Java to keep the Object around. Otherwise, the memory used in that Object will go away (reclaimed by the Garbage Collector). And, with the reference, you can trigger actions on the Animal (like simon.eat() and simon.sleep()). Without the reference, you couldn't do this. Anything you work on in any program must stay around by having some reference to it (like you did above).
The Object reference between Animal and Cat shows that "I can treat Cat, Dog, Elephant, Eagle, Moose, etc. any animal that is an Animal like an Animal." (This is called polymorphism.)
So, an Animal can have actions like eat() and sleep() because all Animals can eat() and sleep(). But, an Eagle can also fly(). So, Eagle has the fly() action defined. And, you wouldn't put the fly() action in Animal because not all Animals can fly().
This can be useful in cases in many different cases (although as a general term this refers to Polymorphism), one I can think of is a Parent array creation. (Parent being the Animal class in this case, which is further extended by Cat).
Animal[] animals = new Animal[] { simon, dog };
You can reference every item in the list simply by using Animal parent type, you don't have to worry about it being further of type Dog or Cat etc.
So suppose you iterate over this and call the sound()/voice() method for each element. If the method is defined in the Parent class (i.e. Animal), you can simple do:
for (Animal animal : animals) {
System.out.println(animal.voice());
}
In java generic I understood what are the meanign of wild card, super and extends, but didn't get why does not allow me to add anything, and why allows me to add upto SomeType in hierarchy, but not above in the hierarchy?
class Animal {}
class Cat extends Animal{}
following method can take list of Animal or sub of Animal i.e Cat, but nothing else
and I am not allowed to add anything, if try to add, compiler stops me why ?
void addAminal(List<? extends Aminal> aList){
aList.add(new Cat()); // compiler error
aList.add(new Animal()); // compiler error
}
Now following method can take any list of Animal or any super type of Animal, but no sub type of Animal, and I can add objects upto Animal or lower in hierarchy, so when I try to add Object, compiler complains why ?
void addAnimal(List<? super Animal> aList){
aList.add(new Animal()); // no error
aList.add(new Cat()); // no error
aList.add(new Object()); // compiler error why ?
}
Thanks
Arya
Suppose you defined a new class:
class Tabby extends Cat {}
And then you did the following:
List<Tabby> aList = new ArrayList<Tabby>();
addAnimal(aList);
There's no surprise that this list should not have an Animal or even a Cat that isn't a Tabby, yet if the compiler didn't flag the error, that's what you would have.
The reason is that hou've specified addAnimal to take a list of something that extends Animal, but that something could be highly restrictive. This, however, would compile:
void addAnimal(List<Animal> aList){
aList.add(new Cat()); // OK
aList.add(new Animal()); // OK
}
The use of super also would work, because an instance of either Cat or Animal is an instance of any superclass of Animal.
List<? extends Animal> means List<X> where X is an unknown subtype of Animal.
Therefore it has methods
void add(X item);
X get(int i);
You can't call add(cat), because we don't know if Cat is a subtype of X. Since X is unknown, the only value that we knows is a subtype of X is null, so you can add(null) but nothing else.
We can do Animal a = list.get(i), because the method returns X and X is a subtype of Animal. So we can call get(i) and treat the return value as an Animal.
Conversely, List<? super Animal> means List<Y> where Y is an unknown super type of Animal. Now we can call add(cat), because Cat is a subtype of Animal, Animal is a subtype of Y, therefore Cat is a subtype of Y, and add(Y) accepts a Cat. On the other hand, Animal a = list.get(0) won't work now, because Animal is not a super type of the return type Y; the only known super type of Y is Object, so all we can do is Object o = list.get(0).
The generics only allow you to add an object of the type (or a subtype) of the type given as type parameter. If you put <? extends Animal> it means the list has SOME type that is a subclass of animal. Since you are trying to add a Cat to it, you have to be sure that it is indeed a list of Cats, and not of Dogs.
Basically, when you use a wildcard you will not be able to add new items to such a list (note: I don't have full knowledge and this might not be fully correct, but it seems like this. Forgive me if I'm wrong)
If you want to be able to add any Animal to the list, just use List<Animal>.
Well, When you say ArrayList< ? extends Animal > you are specifying that this list will contain any specific type (as ? refers to a specific/definite type) that is of type Animal or anything inherited from Animal but something definite. So ultimately, as the generics are implemented with Eraser concept (which replaces every generic type in the program by a non-generic upper bound), this list is supposed to contain a specific type but due to ( < ? extends Animal >) you don't know which specific type is that. And hence you are not allowed to add even though types are inherited from Animal.
But when you say ArrayList< ? super Animal >, it means the arraylist contains specific type derived from Animal that is, objects whose base or super type is Animal. Hence it is safe to pass a Animal or anything derived from Animal into this list. The list is treated that way and it is allowed to add objects as mentioned. And hence it works.
Hope it helps!