Java Generics: How to avoid a mess in class header - java

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

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

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

Create arraylist of class and interface

I'm attempting to create an ArrayList (so java, obviously) with type TileEntity (yes this is a minecraft mod). But I also need the objects added to the ArrayList to implement a certain interface.
The first option that came to mind was creating an abstract subclass of TileEntity that implemented interface, and using that as the ArrayList type. But given the fact that people normally create their own subclasses of TileEntity and use those as the class they normally subclass, and I want people to be able to hook into my mod, I can't expect them to subclass anything besides TileEntity.
My current solution is to check if(object instanceof MyInterface) before adding, but that seems ugly. Surely there's a way to set the type of an ArrayList to require that an object be both a subclass of TileEntity and an implementor of MyInterface.
You can make generic the method or class where the ArrayList is used. For example, a generic method:
public <T extends TileEntity & MyInterface> void doStuffWith(T obj) {
List<T> yourList = new ArrayList<T>();
yourList.add(obj);
...//more processing
}
And a generic class:
public class ArrayListProcessor<T extends TileEntity & MyInterface> {
List<T> theList;
public void processList(T obj) {
theList.add(obj);
...
}
public void someOtherMethod() {
T listElem = theList.get(0);
listElem.callMethodFromTileEntity();//no need to cast
listElen.callMethodFromMyInterface();//no need to cast
}
}
...//somewherein your code
//SomeObj extends TileEntity and implements MyInterface
ArrayListProcessor<SomeObj> proc = new ArrayListProcessor<SomeObj>();
You could add whatever methods of TileEntity you need to your interface, and just make the ArrayList of your interface. There is probably some fancy way of using generics to solve the problem in a better way, but I'm unsure how.
EDIT: dcernahoschi's solution is much better.

Java compile error with generics

public class IRock
{
public List<IMineral> getMinerals();
}
public class IMineral { ... }
public class SedimentaryMineral implements IMineral { ... }
public class SedimentaryRock implements IRock
{
private List<SedimentaryMineral> minerals;
#Override
public List<SedimentaryMineral> getMinerals()
{
return minerals;
}
}
Getting a compiler error:
Type mismatch: cannot convert from List<SedimentaryMineral> to List<IMineral>.
I understand that I can't convert an impl back to its API interface (because an API is just than - an API). But I'm confused as to why I'm getting a compiler error! Shouldn't Java honor the fact that SedimentaryMineral is an impl of IMineral and allow this?!?
Along with an explanation as to why I'm getting this compiler error, perhaps someone could point out why my approach here is "bad design" and what I should do to correct it. Thanks in advance!
Imagine if this compiled:
List<SedementaryMineral> list = new ArrayList<>();
list.put(new SedimentaryMineral());
List<IMineral> mineralList = list;
mineralList.add(new NonSedimentaryMineral());
for(SedementaryMineral m : list) {
System.out.println(m); // what happens when it gets to the NonSedimentaryMineral?
}
You have a serious issue there.
What you can do is this: List<? extends IMineral> mienralList = list
The problem is that Java generics are not covariant; List<SedimentaryMineral> does not extend/implement List<IMineral>.
The solution depends on precisely what you wish to do here. One solution would involve wildcards, but they impose certain limitations.
Here is what will work for you:
interface IRock
{
public List<? extends IMineral> getMinerals();
}
interface IMineral { }
class SedimentaryMineral implements IMineral { }
class SedimentaryRock implements IRock
{
private List<SedimentaryMineral> minerals;
public List<? extends IMineral> getMinerals()
{
return minerals;
}
}
Here I am using wildcard to denote that I allow list of everything that extends the basic interface to be returned from getMinerals. Note that I also changed some of your classes to interfaces so that everything will compile (I also removed the accessors of the classes so that I can put them in a single file, but you can add them back).
First, your code would work if you done something like
...
public interface IRock
{
public List<? extends IMineral> getMinerals();
}
...
Second, you can't do this directly because you wouldn't be able to guarantee type safety from what you insert inside your list. So, if you want anything that could extend Mineral inside your rock, do what I showed above. If you want that only a specific type of be inserted inside a rock, do something like
public interface IRock<M extends IMineral> {
public List<M> getMinerals();
}
public class SedimentaryRock implements IRock<SedimentaryMineral> {
public List<SedimentaryMineral> getMinerals()
{
return minerals;
}
}
You need to understand why this cannot work in general, and why it is a good thing to have the compiler complain here.
Assuming we have a class ParkingLot implements Collection<Cars> and since Car extends Vehicle, this would automatically make the ParkingLot also implement Collection<Vehicle>. Then I could put my Submarine into the ParkingLot.
Less funny, but simpler speaking: a collection of apples is not a collection of fruit. A collection of fruit may contain bananas, while a collection of apples may not.
There is a way out of this: using wildcards. A collection of apples is a collection of "a particular subtype of fruit". By forgetting which kind of fruit it was, you get what you intended: you know it's some kind of fruit you get out. At the same time, you can't be sure that you are allowed to put in arbitrary fruit.
In java, this is
Collection<? extends Fruit> collectionOfFruit = bagOfApples;
// Valid, as the return is of type "? extends Fruit"
Fruit something = collectionOfFruit.iterator().next();
// Not valid, as it could be the wrong kind of fruit:
collectionOfFruit.put(new Banana());
// To properly insert, insert into the bag of apples,
// Or use a collection of *arbitrary* fruit
Let me emphasize the difference again:
Collection<Fruit> collection_of_arbitrary_fruit = ...;
collection_of_arbitrary_fruit.put(new Apple());
collection_of_arbitrary_fruit.put(new Banana());
Must be able to store any fruit, apples and bananas.
Collection<? extends Fruit> collection_of_one_unknown_kind_of_fruit = ...;
// NO SAFE WAY OF ADDING OBJECTS, as we don't know the type
// But if you want to just *get* Fruit, this is the way to go.
Could be a collection of apples, a collection of banananas, a collection of green apples only, or a collection of arbitary fruit. You don't know which type of fruit, could be a mix. But they're all Fruit.
In read-only situations, I clearly recommend using the second approach, as it allows both specialized ("bag of apples only") and broad collections ("bag of mixed fruit")
Key to understanding this is to read Collection<A> as Collection of different kind of A, while Collection<? extends A> is a Collection of some subtype of A (the exact type however may vary).

Why would you use a generic type specifier when extending a Java class?

I just wonder what usage the following code has:
public class Sub extends java.util.ArrayList<String> {...}
There is no any compiling restriction on the generic constraint java.util.ArrayList<String>.
The compiler does place restrictions on other code based on the type parameter in this case.
This will compile
public class Sub extends java.util.ArrayList<String> {
void addTwice(String s) { this.add(s); this.add(s); }
}
but this will not
public class Sub extends java.util.ArrayList<String> {
void addTwice(Object x) { this.add(x); this.add(x); }
}
Let's say you were making an index for a book, but you don't know how many indices you will need. You could make a class BookIndex extends ArrayList<String> or if you want to get really picky: BookIndex extends ArrayList<IndexEntry>.
/e1
Also, when a one Class extends a generic Class like ArrayList<String> you can grab the String out from the generic declaration, unlike if you had a class ArrayList<T>. In ArrayList<T> you would never be able to figure out what the T is.
You can extend class ArrayList, but it is not something that you should normally do.
Only ever say "extends" when you can truthfully say "this class IS-A that class."
Remember, Its not a good practise to extend the standard classes
Why not use like this ?
public class Sub {
List<String> s = new ArrayList<String>();
// ..
// ...
}
If you do that you can add to the basic functionality of an ArrayList or even change its normal functionality.
For example, you can override the add() method so that it will only add emails to the list.

Categories

Resources