How to "safely" use polymorphism with generic template? - java

I have an interface that a few of classes implements it:
interface Vehicle {}
class Car implements Vehicle {}
class Bicycle implements Vehicle {}
And I have another interface that has getter and setter of this:
interface Person {
public ArrayList<Vehicle> getVehicles();
public void setVehicles (ArrayList<Vehicle> vs);
}
And a few of classes implement it:
class CarOwner implements Person {
public ArrayList<Car> cars;
public ArrayList<Car> getVehicles() {
return cars;
}
public void setVehicles(ArrayList<Car> cars) {
this.cars = cars;
}
}
class BicycleOwner implements Person {
//...
}
However it complains the setter has the same erasure as the interface but does not override it. I had to change the ArrayList<Vehicle> to ArrayList, and cast it in the function, but it is not "safe" (I know it should work but others may not). How to deal with this situation?
Edit:
As I changed my code to the answers, it stopped complaining. But when I use it, e.g.
//I have an arraylist of Person
ArrayList<Person> persons;
//...
Vehicle first = persons.get(0).getVehicles().get(0);
The first line it complains the type requires a parameter... But it could be any type of person because the Arraylist persons can contain all types of Person.. How can do this? Or it has to leave it without template parameter?
Edit2:
I updated the answer down there and found when I call the setter it raises error again...
Person<?> personA = persons.get(0);
ArrayList<Vehicle> vs = new ArrayList<Vehicle>();
//vs.add(carA);
personA.setVehicles(vs);
The last line gives error: This gives error saying The method setVehicles(ArrayList<capture#6-of ?>) in the type Post<capture#6-of ?> is not applicable for the arguments (ArrayList<Vehicle>). I tried to change to ArrayList<?> but it does not work...

Your problem is that your interface is declared to accept an ArrayList<Vehicle>, but you are trying to implement it with a method which takes an ArrayList<Car>. Someone could call your interface and legally hand it a list of some other type extending vehicle, so your code won't compile.
What you want to do is make your Person interface generic, so that it can take any type which extends Vehicle, then make CarOwner be a class implementing Person<Car>:
interface Person<T extends Vehicle> {
public ArrayList<T> getVehicles();
public void setVehicles (ArrayList<T> vs);
}
class CarOwner implements Person<Car> {
public ArrayList<Car> cars;
public ArrayList<Car> getVehicles() {
return cars;
}
public void setVehicles(ArrayList<Car> cars) {
this.cars = cars;
}
}
Edit to respond to the question's edit
Well, if you're going to have a raw ArrayList<Person>, you are really just moving your type error from the class definition to the use of it. Once you parameterize Person, you need to also use it that way:
ArrayList<Person<?>> persons = new ArrayList<>();
// ...
Vehicle first = persons.get(0).getVehicles().get(0);
You can learn more about wildcards in Java here.
Edit2
Yes, the drawback of wildcards is the compiler doesn't know the type. In this case you can just use the Vehicle interface, since you do know the type everything is extending:
ArrayList<Person<Vehicle>> persons = new ArrayList<Person<Vehicle>>();
// ...
Person<Vehicle> personA = persons.get(0);
ArrayList<Vehicle> vs = new ArrayList<Vehicle>();
//vs.add(carA);
personA.setVehicles(vs);

If your Person instances are intended to own vehicles of a particular type, why not introduce a type parameter for that type?
interface Person<V extends Vehicle> {
ArrayList<V> getVehicles();
void setVehicles (ArrayList<V> vs);
}
class CarOwner implements Person<Car> {
public ArrayList<Car> cars;
public ArrayList<Car> getVehicles() {
return cars;
}
public void setVehicles(ArrayList<Car> cars) {
this.cars = cars;
}
}

If you want the generic type to be able to vary by implementing class, you'll need to make Person generic. Something like:
interface Person<V extends Vehicle> {
ArrayList<V> getVehicles();
}
class CarOwner implements Person<Car>
#Override
public ArrayList<Car> getVehicles() {
return cars;
}
}
Note also that defining such a specific type as ArrayList in an interface is poor design; there's almost never any reason to restrict return types that narrowly. In this case, if you really needed to have random-access characteristics, you could return RandomAccessList, but List or even Collection is generally a better option.

Make your interface generic.
interface Person<T extends Vehicle> {
public ArrayList<T> getVehicles();
public void setVehicles (ArrayList<T> vs);
}
class CarOwner implements Person<Car> {...}
In response to edit, use generic wildcard
//I have an arraylist of Person
ArrayList<Person<?>> persons;
//...
Vehicle first = persons.get(0).getVehicles().get(0);

Related

Java: when to use generics method and when explicit method

Lets suppose we have 10 types of Car:Bmw, Renault etc. And we have Repo. So there are two ways for developing API of Repo:
The first way:
class Repo{
public <T extends Car> T getCarByType(Class<T> clazz){..}
}
The second way:
class Repo{
public Bmw getBmw(){..}
public Reno getRenault(){..}
...
}
Firstly I thought that I should follow the first way as it lets write less code and -> it is better for supporting. And besides, I thought that if I have 20 types of car the first way is obvious advantage (less code).
However, as the number of car is growing - you start to forget what car you have. When you follow the second way - you have clear API of the repo and the types.
So,could anyone explain when to use which method?
The first way will produce less duplicate code. But if you want to model the types of cars, you could use an enum
enum Cars {
BMW(Bmw.class),
RENO(Reno.class)
;
Class<? extends Car> type;
Cars(Class<? extends Car> type){
this.type = type;
}
Class<? extends Car> getType() {
return type;
}
}
And the access the car using this enum
public <T extends Car> T getCarByType(Cars car){
Class<T> type = car.getType();
...
}
First if your cars aren't so different you may use only one class Car that will have a type property which will allow you to make difference between cars type, however if you wan't to continue with your solution I think you may use both of the ways, you can create a generic Repo which will have all code used to get Data then you can make on top of it a specific layer that will expose methods to retrieve each car by it's type
Generic Repo
class Repo{
public <T extends Car> T getCarByType(Class<T> clazz){..}
}
RepoFacade
class RepoFacade{
public Bmw getBmw(){
Repo<Bmw> = new Repo<>();
return repo.getCarByType(BMW.class);
}
public Reno getReno(){..}
}
I would go with something simpler. No polymorphism based on brand, because it is just a label, it does not add any behavior:
public class Car {
public enum Make { BMW, ... };
public Make make;
public Color color;
public int year;
// Cars have other properties, I suppose
}
public class Repo {
private List<Car> cars = new ArrayList<Car>;
// This could also return a list of all cars of that model,
// because there is no reason to have exactly one of each make.
#Nullable
public Car findByMake(Car.Make make) {
for (Car car : cars) {
if (car.make == make) {
return car;
}
}
return null;
}
}

How do I properly extend this abstract class?

Hi I'm inexperience with Java. I understand the concepts of inheritance but I think the syntax is eluding me. I'm seeking some help to get me started in extending this abstract class:
I need to create a concrete object from it.
What this class should do is take in a type during initialization and store a list of objects of that type. Sort them and then return a list of n top objects when showTopN is called.
I have not started implementing the logic yet.
abstract class Foo<T extends Comparable<T>> {
int n;
Foo(int n){ // constructor; sets object property n
this.n = n;
}
abstract void push(T object); //object method to store a new object in the list
abstract List<T> showTopN(); // object method to return top n entries in the list, sorted.
}
I've tried to extend this into a concrete object this way:
class ConcreteFoo extends Foo {
private List<Foo> fooList;
public void push(Foo object) {
}
#Override
public List<Foo> showTopN() {
return fooList;
}
#Override
public int compareTo(ConcreteFoo other) {
return 0;
}
}
But the compiler is complaining that I have not overridden the push method.
What is wrong?
There are two things going on here. One is the "abstractness" of Foo, but the other is the Generics. You have neglected the generics aspect.
If you know the type of object that your Concrete foo cares about, you can just use that:
class ConcreteFoo extends Foo<SomeKnownClass> {
private List<SomeKnownClass> list = new ArrayList<SomeKnownClass>();
void push(SomeKnownClass skc) {}
List<SomeKnownClass> showTopN() { return list; }
}
Now, if you don't know the type of it, you can still use generics:
class ConcreteFoo<T extends Comparable<T>> extends Foo<T> {
private List<T> list = new ArrayList<T>();
void push(T skc) {}
List<T> showTopN() { return list; }
}
Note that neither Foo nor ConcreteFoo implement Comparable, so you don't need the compareTo method.
The push method specifies that it will accept a T object, which is Foo's generic type, which you haven't declared. If you want Foo to be a List of itself, which I'm not certain that you do, you'd have to declare it as
class ConcreteFoo extends Foo<Foo> {
But I think you need to re-examine your basic principles.
You're conflating a container with the objects that it contains. The class structure that you want is something like:
class Foo implements Comparable<Foo> { ... }
abstract class GenericContainer<T> {
abstract void push(T object); //object method to store a new object in the list
abstract List<T> showTopN(); // object method to return top k entries in the list, sorted.
}
class FooContainer extends GenericContainer<Foo> {
private List<Foo> fooList;
...
}
Your showTopN method can then be something like:
public List<Foo> showTopN() {
return Collections.sort(fooList).subList(0, n);
}

How to implement a method from a derived class that does not require cast

Let's suppose I have 2 classes Vehicle and Car
public class Vehicle {
public List<? extends Vehicle> getList() {
return new ArrayList<Vehicle>();
}
}
#Override
public class Car extends Vehicle {
public List<? extends Vehicle> getList() {
return new ArrayList<Car>();
}
}
If I want to get a list from a class that derives Vehicle is there a way I can use getList() without having to cast?
This doesn't work(and I understand why:
List<Car> list = new Car().getList();
And this does:
List<Car> list = (List<Car>)new Car().getList();
Is there a way to prevent this?
Change the getList() method in the Car class to return a List<Car> instead.
#Override
public List<Car> getList() {
return new ArrayList<Car>();
}
#Marv is right and you can change your assignment also as below
List<? extends Vehicle> list = new Car().getList();

Setting Java generics of iterators

I've noticed something funny Java does (or at least Netbeans) when I use classes implementing ArrayList and changing the generics type of the elements. I basically created an abstract class that extends ArrayList and some subclasses that are supposed to work with String objects (so something like ArrayList<String>). One of the things I did to try to achieve that was this:
public abstract class A extends ArrayList {
...
}
#Override
public abstract class B extends A {
public Iterator<String> iterator() {
return super.iterator();
}
}
Another one was this:
public abstract class A extends ArrayList {
...
}
public abstract class B<String> extends A {
#Override
public Iterator<String> iterator() {
return super.iterator();
}
}
The first one overrides successfully the iterator() method assigning a String value to it. The other one somehow cancels out the type casting. The funny thing is that none of them works when it comes to for loops. This receives type Object instead of String.
for (String s : B) {
...
}
Do you have any idea why this happens and how can I fix it without implementing my own iterator?
Not sure what you are trying to do but if I understand correctly you want a class that extends ArrayList and has a Generic type of String... Perhaps you are looking for this:
public abstract class A<T> extends ArrayList<T> {
...
}
public abstract class B extends A<String> {
...
}
Then in your code, this:
B myList = ...;
for ( String s : myList ) {
...
}
Will work just fine. Though I think you could come up with a much better solution. Do you have more specifics about your problem?
Use composition instead of Inheritance
public class A implements Iterable<String>{
List<String> myList = new ArrayList<String>();
//do operations on myList
public Iterator<String> iterator() {
return myList.iterator();
}
}
If you extend generic class you should care about generics. I mean that your declaration should look like
public abstract class A extends ArrayList<String> {
...
}
if you want to use strings or
public abstract class <T> A extends ArrayList<T> {
...
}
if you want your class to be generic.
In both cases you do not have to override iterator() method: you can invoke its from super class and it will return you "good" iterator. Your declaration is equivalent to
public abstract class A extends ArrayList<Object> {
...
}
This is the reason for "strange" behavior.
BTW may I ask you why are you extending ArrayList? It really sounds strange.
OMG this is terrible:
public abstract class B<String> extends A {
#Override
public Iterator<String> iterator() {
return super.iterator();
}
}
String is not the class String, rather, you are declaring a new type variable called String (like T) that shadows the class String

Java collections covariance problem

Lets say we have a program which contains such classes:
public interface AbstractItem {
}
public SharpItem implements AbstractItem {
}
public BluntItem implements AbstractItem {
}
public interface AbstractToolbox {
//well the problem starts here...
public List<AbstractItem> getItems();
}
public ExpensiveToolbox implements AbstractToolbox {
private List<SharpItem> items = new ArrayList()<SharpItems>;
public List<SharpItem> getItems() { return this.items; }
}
public CheapTooblox implements AbstractToolbox {
private List<BluntItem> items = new ArrayList()<BluntItem>;
public List<BluntItem> getItems() { return this.items; }
}
Easy, right? Well lets say we now want to make a method like this (in some random class):
public void doImportantStuff(AbstractToolbox toolbox) {
//important stuff!
//this obviously won't work
List<AbstractToolbox> items = toolbox.getItems();
//do some stuffwith all items
}
Now the problem is that in Java collections with generics aren't covariant (hope that's the term I'm looking for) and I can't assign an ArrayList<ExpensiveToolbox> to a List<AbstractToolbox>. The only solution I can see here is to duplicate the code and do a version for each type, but that would obviously suck (what if we had more classes implementing AbstractToolbox with different lists?). Oh obviously the second solution would be to drop the generics and make a normal List, but is it a good practice?
Are there any design pattern/practices to tackle such problems?
#Edit: ok so I might not be precise enough. I want all the classes which extend AbstractToolbox to have a List of certain classes which extend AbstractItem and then I want a method that will take an AbstractToolbox as a parameter and do something on the items in its list (using the classes that would be defined in AbstractItem so all the items of every possible list would actually have them).
You're probably going to need to take a look at using wildcard types for generics. Here's a quick link: What is PECS (Producer Extends Consumer Super)?
Quick answer: change the type to List<? extends AbstractItem>
Why can't you just assign this?
Imagine the code here...
List<AbstractItem> foo = new ArrayList<SharpItem>();
foo.add(new BluntItem());
The static typing says this should work... but you can't do that! It would violate the ArrayList's type. That's why this is disallowed. If you change it to
List<? extends AbstractItem> foo = new ArrayList<SharpItem>();
you can then do the assignment, but never add anything to the list. You can still retrieve elements from the list, however, as AbstractItems.
Is just using List (bare type) a good solution?
No, definitely not :-p
Here are a couple of extra ideas. Leave everything the same, but use this:
interface AbstractToolbox {
public List<? extends AbstractItem> getItems();
}
This basically says that the abstract class' items are an unknown type, but subclasses can make it concrete. This would require you to call getItems() on a reference of type ExpensiveToolbox or CheapToolbox to be able to retrieve a list that allows you to add items, etc.
ExpensiveToolbox toolbox = new ExpensiveToolbox();
AbstractToolbox absTB = toolbox;
List<? extends AbstractItem> items1 = absTB.getItems(); //fine
List<SharpItem> items2 = absTB.getItems(); //compile error
List<SharpItem> items3= toolbox.getItems(); //fine
Alternatively, you could just type AbstractToolbox:
public interface AbstractToolbox<T extends AbstractItem> {
public List<T> getItems();
}
public ExpensiveToolbox implements AbstractToolbox<SharpItem> {
public List<SharpItem> getItems() { //...
}
public interface AbstractItem
{
}
public class SharpItem implements AbstractItem
{
}
public class BluntItem implements AbstractItem
{
}
public interface AbstractToolbox<T extends AbstractItem>
{
public List<T> getItems();
}
public class ExpensiveToolbox implements AbstractToolbox<SharpItem>
{
private List<SharpItem> items = new ArrayList<SharpItem>();
public List<SharpItem> getItems() { return this.items; }
}
public class CheapToolbox implements AbstractToolbox<BluntItem>
{
private List<BluntItem> items = new ArrayList<BluntItem>();
public List<BluntItem> getItems() { return this.items; }
}
public void doImportantStuff(AbstractToolbox<?> toolbox)
{
List<? extends AbstractItem> items = toolbox.getItems();
for(AbstractItem item : items)
... ;
}

Categories

Resources