Java: when to use generics method and when explicit method - java

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;
}
}

Related

Pass arguments that extends from same abstract class to function

I finished some exercise but I wanted to optimize it a little bit.
I have two classes name "U1" and "U2", both extend the same abstract class called "Rocket" and I have two functions called "loadU1" and "loadU2" that returns ArrayList (U1 or U2) and i wanted to optimize it to one function without success. I tried the generic type, but I cant figure out how should I know which type to return or cast to. Does it possible?
Note: I didn't share some code that I have tried because I got no idea where should I start even (JAVA Newbie).
This is the strcture of the classes:
public class U1 extends Rocket {}
public class U2 extend Rocket {}
abstract class Rocket implements SpaceShip {}
These are the current functions:
public ArrayList<U1> loadU1(ArrayList<Item> items) {
ArrayList<U1> u1Rockets = new ArrayList<>();
return u1Rockets;
}
public ArrayList<U2> loadU2(ArrayList<Item> items) {
ArrayList<U2> u2Rockets = new ArrayList<>();
return u2Rockets;
}
Maybe you are looking for something like this:
public <T extends Rocket> List<T> load(List<Item> items) {
List<T> rockets = new ArrayList<>();
return rockets;
}

Java Generics: How to avoid a mess in class header

I am working with java generics and I want to avoid a mess in my class headers.
// The car saves a generic list
class Car<L>{
ArrayList<L> exampleList=new ArrayList();
public void ArrayList<L> getExampleList(){
return exampleList;
}
}
class Mercedes extends Car<Engines> ...
class Porsche extends Car <Wheels>...
class Warehouse<T extends Car>{
T car;
// I want to work with a generic List here
public void useListFromCar(){
// This returns a simple ArrayList but not a ArrayList<L>
car.getExampleList();
}
}
I need to be able to work with a generic list, not just a ArrayList. The only way I know of to solve this results in a mess in my class headers. Also, it would be redudant information in the header. The specific child car already knows the type of the List. Why should the Warehouse need to know about that.
// Redundant and messy header :(
class Warehouse<L, T extends Car<L>>{
T car;
public void useListFromCar(){
// This returns the desired ArrayList<L>
car.getExampleList();
}
}
I think you can imagine that this becomes quite unmanageable with bigger classes. Is there a way to avoid this?
the way you have done does't have problem.
the problem is the way you abstract it :
why Cars have generic type such as Engines, Wheel for different brand?
Engines and Wheels are cars' components.
you can write several interfaces named Engine, Wheel, and implement concrete classes of them.
in your Car class, it should contains these components. and the generic type could be brand name.
all the codes are generated from the real world common sense.
if you write something that doesn't make sense, then it will be in a mess for sure.
here is a sensible example:
interface Car<L>{
List<Wheel> wheels;
Engine engin;
//get set method
}
interface Wheel{
String getBrand();
String getquality();
...
}
interface Engine{
String getHorsePower();
String getSize();
...
}
class MuscleCar implements Car<Mercedes>{
...
}
class RacingCar implements Car<Mercedes>{
...
}
class Warehouse<T extends Car>{
List<T> cars;
// I want to work with a generic List here
public void useListFromCar(){
// This returns a simple ArrayList but not a ArrayList<L>
for(T car: cars){
car.getBrand();
car.getType();
car.getEngine(); //etc..
}
}
}

Confused with generics and subtyping

So, let's say we have a simple interface
public interface ICopyable<T> {
void copyFrom(T original);
}
Which basically adds a copyFrom method with a parameterized type. And you have an interface extending it:
public interface ISomeObject<T> extends ICopyable<T> {
String getType();
}
The interface isn't adding anything valuable of course apart of it's type, but let's pretend there are some useful methods in it. The idea is still the same - all objects of this interface have a getType() method and they can copy one object of THE SAME TYPE from one to the other.
Now let's have two implementations of this interface, and the second implementation inherits from the first one:
public static class ActualObject1 implements ISomeObject<ActualObject1> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1 {
Object data2;
#Override public void copyFrom(final ActualObject1 original) {
super.copyFrom(original);
// oh no! i've just realized that i'm not copying the ActualObject2!
}
}
So the second's object(ActualObject2) is supposed to extend the ActualObject1, but if it's done this way it can't implement the right "copyFrom" method, as the first class implements the interface ISomeObject for itself only. And it obviously wants to do it somehow to allow copying of ActualObject2's to each other. But how?
It can't just declare implements ISomeObject as it'll clash with it's parent's implementation type.
So you'll want to do something like that maybe?
public static class ActualObject1<T extends ActualObject1> implements ISomeObject<T> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1<ActualObject2> {
Object data2;
#Override public void copyFrom(final ActualObject2 original) {
super.copyFrom(original);
this.data2 = original.data2;
}
}
Basically parameterizing the class1, and class2 specifies itself as a parameter. It all works fine, you can create instances of both types:
ActualObject1 obj1 = new ActualObject1();
However there's one "little" problem - the obj1 has a raw type. The full declaration looks rather silly:
ActualObject1<ActualObject1> obj2 = new ActualObject1<>();
But it works. However the "raw type" nature of this class can bite for example in this scanario:
public static class SomeOtherParameterizedClass<T extends ISomeObject<T>> {
void copyObjects(T obj1, T obj2) {
obj1.copyFrom(obj2);
}
}
So you're creating some random class parameterized by <T extends ISomeObject<T>>. And you can in theory say it like that: <T extends ISomeObject> but then you won't be able to use T in the "copyFrom" safely. In other words - it's a valid class parameterization, it has a point.
But then you can't parameterize it for ActualObject1:
SomeOtherParameterizedClass<ActualObject1> a1 = new SomeOtherParameterizedClass<>();
Yea - doesn't work. Hint:
SomeOtherParameterizedClass<ActualObject2> a2 = new SomeOtherParameterizedClass<>();
Works just fine...
So what's the right approach here? I'm more interested in retaining the type safety as much as possible, as for sure you can just use raw types all the way and don't worry about anything, but it's for the weak! :-)
We're in statically typed language so it's somewhat of an academic question - what's the right way of designing this class hierarchies with generics? Is occasionally using raw-types actually required for those to work?

Generic interface to avoid casting

I want to have an interface that returns different type of interfaces based on what is requested. To explain the issue I used a car example.
public interface ICar{
public ? getCar(String carName);
}
public class Car implements ICar{
public ? getCar(String name){
// Depending on the car name return ICommonCar or IBMW or IAudi...
return new BMW();
Or return new Audi();
...
}
And different users classes will get the ICar interface where they can invoke getCar(carName). Eg.
First class can request:
IBMW mycar = ICar.getCar(BMW);
Second class requests:
IAudi myCar = ICar.getCar(Audi);
I was thinking to use Java Generics to solve the issue but I think there is something that I am missing. My first approach was to something like below:
public class Car<T>{
public T getCar(String carName){
public T newCar;
if(carName.equals(BMW)){
T = new BMW(); // Shall I cast it T?
}else if(carName.equals(Audi))
T = new Audi();
}...
return T;
}
The code above in using Generics doesn't compile but I just put it to show the intention that I want to achieve. The problem seems simple but I found using Generics to be tricky. Is it possible to solve the above problem using Generics? Thank you in advance!
EDIT:
Please consider that in the example I did not meant to imply that IBMW & IAudi as child interfaces of the ICar interface but rather I was thinking to have the ICar as an entry point where different types of checks will be performed on the request & on the class initiating request. Then implementation of unrelated interfaces like IBMW & IAudi which are child interfaces of ICommonCar would be returned. I should have used a different example.
In order to achieve something like you describe, you are going to make the client do as much work as he would do by performing his own cast. I'm changing your class names to make more sense. To whit:
ICarFactory cf = new CarFactory();
// normal way
IAudi audi = ( IAudi )cf.getCar( "audi" );
// your way
IBMW bmw = cf.getCar( "bmw", IBMW.class );
The difference being that in the former case, you need only return ICar from getCar, whereas in the latter, you need to make the method getCar generic in a variable < T extends ICar > and then cast to T before returning your car. That may not even be legal in all cases.
Meantime you get zero help from the compiler regarding types in either case.
It looks like you are looking for a factory pattern. I try to avoid using strings to specify what I want the factory to make - enums are a good fit.
interface Car {
}
class BMW implements Car {
}
class Audi implements Car {
}
interface MakesCars {
Car makeCar() throws InstantiationException, IllegalAccessException;
}
enum Cars implements MakesCars {
BMW(BMW.class),
Audi(Audi.class);
Class<? extends Car> c;
Cars(Class<? extends Car> c) {
}
#Override
public Car makeCar() throws InstantiationException, IllegalAccessException {
return c.newInstance();
}
}
public void test() throws InstantiationException, IllegalAccessException {
Car bmw = Cars.BMW.makeCar();
}
The Java 8 version of the enum is a little more elegant.
enum Cars implements MakesCars {
BMW(BMW::new),
Audi(Audi::new);
final Supplier<Car> supplier;
Cars(Supplier<Car> supplier) {
this.supplier = supplier;
}
#Override
public Car makeCar() {
return supplier.get();
}
}

How to "safely" use polymorphism with generic template?

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);

Categories

Resources