Java: Generics on data structures and wildcards - java

I'm trying to learn how to use generics and for that, I'm creating my own data structure similar to a list with an Iterator and all.
The uppermost class is declared as:
public class BasicList<A> implements Iterable {
}
It's just a list with nodes and a single pointer to the next element.
There's another list, called DescendingList, that does pretty much the same, only this time around, you're getting another output since the Iterator is different. I'm giving a comparator to this List to get the Iterator to work. The comparator is just an interface:
public interface Bigger<A> {
boolean bigger(A x);
}
DescendingList looks like this:
public class DescendingList<A extends Bigger<A>> extends BasicList<A> implements Iterable {
}
The idea is that it could work with any kind of object that is comparable through the Bigger interface.
Now I have an abstract class:
public abstract class Rock implements Bigger<Rock> {
}
And finally, a normal class which extends the Rock class:
public class Mineral extends Rock {
}
So, the issue at hand is that, while I can easily create a new BasicList filled with Minerals like this:
BasicList<Mineral> min = new BasicList<Mineral>();
I can't do the same with the DescendingList. Whenever I try going with
DescendingList<Mineral> min = new DescendingList<Mineral>();
my IDE (IntelliJ) goes all
"Type parameter "Rock" is not within it's bound; should implement Bigger<Mineral>"
And I don't really get why that happens. I am pretty sure that I've messed up the type parameters somehow and I'm really not sure where. This should be solveable somehow (at least without removing any classes/interfaces - the class headers might be and probably are completely messed up). Thanks in advance for any help you might offer.

A Mineral isn't a Bigger<Mineral>, it's a Bigger<Rock>, and that is incompatible with Bigger<Mineral>, because even though a Mineral is a Rock, Java's generics are invariant. It needs to be a Bigger<Rock> because of how Rock is defined -- implementing Bigger<Rock> -- and that DescendingList declares type parameter A to be a Bigger<A>.
Because it's a consumer (type parameter as a method parameter), the type parameter A needs a lower-bound (super) on its declaration.
class DescendingList<A extends Bigger<? super A>> // ...
This way Mineral will be within its own bound.
As an aside, you are implementing the raw form of Iterable in BasicList; you should supply a type argument there.

Related

Java Generic Type extend multiple classes

How can the type for a generic class extend multiple classes? For example, say I have I generic that I want to be able to take objects that are subclasses of Melons or Berries, but not objects that are subclasses of other subclasses of Fruits?
I was thinking something like
public class MyGeneric<T extends Melons & Berries>
but this does not work as an interface is expected after the ampersand, not a class. Additionally, online I saw the suggestion to make an interface and have T implement the interface, however I do not understand how to make an interface that specifies classes that can be accepted by a generic.
& means and. T extends Melons & Berries means that T must be some type that extends both. You evidently want it to mean or. It doesn't. And <T extends Melons | Berries> is not legal java syntax. For good reason - that would be useless.
You may think: Wait, you can't extend 2 things. Yes, you can - extends in generics refers solely to supertypes; it's like a combination of extends and implements.
For example:
void test(List<? extends Serializable & CharSequence> items) {
}
can be invoked by passing a List<String>. That's because String is a subtype of Serializable, and also a subtype of CharSequence.
The 'point' of this is that when you get an element from this list, you can treat it as either. Because it's and - it's a list of both.
There is no way in java to say: "I want to accept a list of Berries, or a list of melons, but not a list of Apples". The reason there's no way to say that, is because its useless:
If there's a thing you can do to both melons and berries but not bananas, then whatever it is will have to be defined in an interface of sorts. Perhaps Melons and Berries both implement 'SeededFruit', and Banana doesn't. In that case, just write List<? extends SeededFruit> isntead.
If you just want to use aspects of melons and berries that are shared by all fruit (also bananas), just write List<? extends Fruit> - why put up arbitrary restrictions?
If you want to write if (x instanceof Melons) doThing1; else if (x instanceof Berries) doThing2; - java isn't 'meant' for that. There are tons of things java doesn't have that would be really convenient if everybody wrote code like that. But java doesn't have that, will not have that, folks don't usually write their java code like that. Usually if someone does, they're writing very 'bad' code, in the sense that it is overcomplicated and/or hard to maintain and/or hard to adopt in the face of changing requirements, and needlessly so: You could write it differently and avoid those traps.
The way I would do is to group the classes using an interface. Then I use the interface to define the generic class. See below:
interface ForGenerics { }
class Fruit { }
class Melon extends Fruit implements ForGenerics {}
class Berry extends Fruit implements ForGenerics {}
class Apple extends Fruit {}
class MyGeneric<T extends ForGenerics> { }
public class Main{
public static void main(String[] args) {
MyGeneric<Melon> a;
MyGeneric<Berry> b;
// can't make MyGeneric of Apple
// MyGeneric<Apple> c;
}
}

Factory Method - Java vs. Python

I have a question regarding the implementation of Factory Method in Java and Python.
Suppose I want to model 2 kind of animals - dogs and cats, and expose a factory method for their creation.
As I understood, the best practice implementation on Java will be:
abstract class Animal{
public abstract void sound();
}
public class Dog extends Animal{
public void sound(){
System.out.println("Woof");
}
}
public class Cat extends Animal{
public void sound(){
System.out.println("Maw");
}
}
class AnimalFactory {
//use getShape method to get object of type shape
public Animal getAnimal(String animalType){
if(shapeType == null){
return null;
}
if(animalType.equalsIgnoreCase("Dog")){
return new Dog();
} else if(animalType.equalsIgnoreCase("Cat")){
return new Cat();
}
return null;
}
}
However, on Python it will be something like that (source):
class Animal(ABC):
#abstractmethod
def sound():
pass
class Dog(Animal):
def sound():
print('Woof')
class Cat(Animal):
def sound():
print('Maw')
class AnimalFactory():
#staticmethod
def getAnimal(typ):
targetclass = typ.capitalize()
return globals()[targetclass]()
For my question:
It seems that because I use globals() on Python, I can actually use the AnimalFactory in Python, to create any arbitrary class that within the symbol table on run-time, that accepts zero arguments, in contrast to the Java implementation that checks explicitly the class name.
Is this a bad practice or just a "pythonic way"?
Thanks.
Edit:
I want to clarify why I am not feeling comfortable with the python code:
Suppose I want to write another Factory class for other group of classes, it will have exactly the same implementation as the Animal, and theoretically, I could even use the exact Factory class.
I know I may make some people angry by the following statement, but I feel that the 'duck typing' behaviour, and the lack of formal explicit contract in the code, is the root of the illustrated problem and the Python illnesses in big development projects.
If this is just 'for your own use' then I likely would not be too concerned. However, if it's an interface that 'other people' will use, then in my opinion, something calling itself AnimalFactory has no business returning something that's not an Animal. So, I think a check should be made.
I'd suppose a simple way to do it would be to ensure the result from the globals()[targetclass]() call is an instance of Animal.
Well, it is not seems a bad practice to me. However, I would first check if such targetClass exists, So I won't get an error.
Plus, I'm sure there are many ways to make the java factory method more efficient.
e.g. I could create from advance a list of all classes that extends Animal, then when i get animalType variable, I could find a class from the list that is corresponding to that type.
There are many ways and libraries that help to get such a list. One popular option is Reflections, which you can use it like that:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends Animal>> classes = reflections.getSubTypesOf(Animal.class);
Then just go over the set and chose the wanted class.
Edit:
As for your edit, well, I wouldn't call it illness. Python capabilities are great, but sometimes there are features of "You can, but it doesn't mean you should" kind, I guess these are one of them. You can define for each factory a collection of "legal classes" you can create in that particular factory - such a practice can lower down a bit your concerns :)

Extending collection without class body

I need in my program collection in collection in collection. So something like this:
ArrayList<ArrayList<ArrayList<String>>>
You can see this does not look good. Mainly when i am using a lot of generics. So i created something like this:
public class ThreeDimensionArray extends ArrayList<TwoDimensionArray> { }
class TwoDimensionArray extends ArrayList<ArrayList<String>> { }
is this solution bad in some way or is it ok?
It's not great, but it is ok. It's rather masking what you are doing - and its a bit wasteful as you are creating a concrete class to define something that type erasure would have turned into a standard List at compile time.
Really you should be using List rather than ArrayList and the diamond operator, both changes will make the original tidier:
List<List<List<String>>> 3dList = new ArrayList<>();
If you do go down the defined class route at least use generics -
class TwoDimensionArray<T> extends ArrayList<ArrayList<T>> { }
Then you can use it for multiple types.

Creating a Generic that Always Extends the Class Using it

I'm trying to design a special type of collection that may only contain elements of its own type. This collection class must also be extendable. At first glance, this seems like a simple enough task:
public class MyClass<E extends MyClass> implements Collection<E>{
//insert Collection<E> methods here...
}
However, because one of these collections may only contain elements that are instances of its class, the following shouldn't be possible:
public class MySubclass extends MyClass<MyClass>{}
What I need is a version of MyClass that essentially does the following (don't try to compile this one):
public class MyClass<E extends this.getClass()> implements Collection<E>{...}
Is it possible to implement something like this? If so, how can it be done?
I think the signature your are looking for would be something like this:
public class MyClass<E extends MyClass<E>> ...
The problem with this, and why I don't think you can enforce what you want from a declaration standpoint is that you have an infinite loop. If the collection may only contain elements of its own type, then elements can only contain elements of itself, etc, etc.
You now have an infinite loop where you may only contain Collections and at no point will you have a Collection that contains some THING.
Also, remember that at runtime type-erasure does away with all generic type. Therefore, no matter how fancy your generic declaration is at any point someone could do the following and just get a warning:
new MyClass();
That is another reason why trying to enforce this type of structure probably won't work well.
Instead you could put an appropriate check in the add method that would provide runtime enforcement. Still have the infinite loop issue though.
EDIT
To do runtime checking, do the following:
public boolean add(E element){
if (!(element instanceof MyClass))
throw new IllegalArgumentException("Element is not an instance of MyClass");
// rest of add code here
}

Java Generics - explanation needed

I have a question about Java Generics. In code below we have interface B parametrized by another type that must implement interface A.
This code is correct.
Question is: why it does not work with following list() method declaration?
private <X extends A, Y extends B<X>> List<Y> list()
Working code:
public interface A {
}
public interface B<T extends A> {
}
public class Test {
private static class AA implements A {}
private static class BB implements B<AA> {}
private <R extends A, X extends R, Y extends B<X>> List<Y> list() {
return null;
}
private void test() {
List<BB> l = list();
}
}
EDIT:
I've reworked the code. Now we have bird paremetrized by sound it can make. Question is why useless_t is necessary?
public class Test {
public interface Sound {
}
public interface Bird<T extends Sound> {
}
private static class Quack implements Sound {}
private static class Duck implements Bird<Quack> {}
private <useless_t extends Sound, sound_t extends useless_t, bird_t extends Bird<sound_t>> List<bird_t> list() {
return null;
}
private void test() {
List<Duck> l = list();
}
}
My Eclipse IDE does not compile any of your code examples as is. But they do compile when given additional type hints. In the second example, with or without the type parameter useless_t, the following line does not compile for me:
List<Duck> l = list();
But the following does compile for me:
List<Duck> l = this.<Sound, Quack, Duck> list();
With the useless_t factored out, the following compiles, too:
List<Duck> l = this.<Quack, Duck> list();
So it is basically a matter of the compiler not getting the type parameters right, and you need to give the types explicitly.
UPDATE : If you really came across a program where adding the useless_t made a difference, you are on unsafe terrain, and rely on unspecified compiler behaviour.
You ran into an issue where different compilers behave differently, namely Type Inference. The JLS is not entirely clear on where a compiler must infer types, and where it must refuse to infer, so there is wiggle room here. Different versions of the Eclipse compiler and different versions of javac differ in where they do infer types. For javac, this is true even when comparing different 1.5.0_x versions, and the Eclipse compiler usually can infer more than javac.
You should only rely on type inference where all common compilers succeed, and otherwise give type hints. Sometimes, that is as easy as introducing a temporary variable, but sometimes (as in your example) you must use the var.<Types>method() syntax.
Regarding the comment: what if i want method Duck.getSound() to return Quack, not Sound using generics?
Assume the Bird interface had the following method:
public interface Bird<T extends Sound> {
T getSound();
}
Then you could implement it like so:
private static class Duck implements Bird<Quack> {
public Quack getSound() { return new Quack(); }
}
This is one use case for generics - allow implementations to specify concrete types, so that even the superclass can use that type. (The Bird interface could have a setSound(T), or do other stuff with T, without knowing the concrete type of T.)
If a caller only knew that an instance was of type Bird<? extends Sound>, he would have to call getSound like so:
Sound birdSound = bird.getSound();
If the caller knew about Quack, he could perform an instanceof test. But if the caller knew that the bird was really a Bird<Quack>, or even that is was a Duck, then he can write this and it compiles as desired:
Quack birdSound = bird.getSound();
But beware: Generifying too many types used in the interface or superclass brings the risk of overcomplicating the system. As Slanec wrote, Rethink your real design to see whether it's really needed to have so many generics.
I once went too far, and ended up with a interface hierarchy and two implementation hierarchies, based on interfaces like this:
interface Tree<N extends Node<N>,
T extends Tree<N, T>> { ... }
interface SearchableTree<N extends SearchableNode<N>,
S extends Searcher<N>,
T extends SearchableTree<N, S, T>>
extends Tree<N, T> { ... }
I do not recommend to follow that example. ;-)
I'd say: AA implements A, by defining List<AA> l = list() you expect it to extend B<X> which it does not. Anyway, you see how easly you get confused by writing such code. This is just TOO complex.
You have a slight misunderstanding of Java Generics. The thing to remember, and this is a subtle thing, a List<Y> is not about the contents of the list, but a modification of the list itself.
Let's extrapolate a little bit; say I have interface Animal and interface Dog extends Animal and interface Cat extends Animal. (I'll just invent more classes and interfaces as we go along.) Now if I declare a method that returns animals as List<Animal> createList(), there's nothing wrong with the following code:
List<Animal> litter = createList();
Cat tabby = new Tabby();
litter.add(tabby);
Dog poodle = new Poodle();
litter.add(poodle);
That's because a Dog is an Animal, and a Cat is an Animal; the method signature of add on type List<Animal> is add(Animal); we can call add with any valid instance of Animal, as expected. But the type parameter on List does not modify or restrict the contents of the list, it modifies the type of the list itself; and a "list of cats" is not a "list of animals", neither is a "list of dogs". Even if the createLitter() method actually returns a new ArrayList<Animal>() that contains only instances of Parrot, the above code is fine. What you cannot do however is 'narrow' the type of the list. For example, this is a compile error:
List<Bird> birds = createList(); // does not compile
Imagine if it were allowed, and createList returned a "list of animals" that contained our tabby; the following would result in a class cast exception:
Bird leaderOfTheFlock = birds.get(0);
You also cannot 'widen' the type of list. Imagine if it were possible:
List<Object> things = createList(); // does not compile
The reason this is not allowed either is that code could now add a new Integer(0) to things - because an Integer is an Object. Clearly that's not what we want either, and for the same reason - a "list of animals" is not a "list of objects". The type parameter "Animal" on List<Animal> modifies the type of the list itself, and we are talking about two distinct types of lists. This leads us to the first consequence of that point - generic types do not follow the inheritance (is-a) hierarchy.
Without knowing more of what you want to do it's hard to go from here and stay relevant. I don't mean to be harsh, but it looks like you started throwing generics at your code to see if something would work. I struggled for years with Generics. Even after running across a blog that explained this subtle point I had to recreate quite a few variations of the above to reinforce the lesson, looking for various ways I would end up with a class-cast-exception if I broke the rules. Likely the solution to your problem is that other parts of the code are not well defined with respect to the strict type system you are trying to introduce, and the generics problems you see are only a symptom of that. Try to reduce the generics and rely more on composition and inheritance. I still shoot myself in the foot occasionally by going off the generic deep end. It's also helpful to try and remember the point of generics is not to eliminate casts, but to make type information available to the compiler as an aid to verifying the correctness of how your code deals with the types; or in other words it turns runtime errors (class cast) into source / compile-time errors, so it is important to try to keep in mind the distinction between what type information you have at compile-time (which is limited, even with generics) and what type information you have at runtime (which is the full type information of the instances).

Categories

Resources