Java PECS fail for list of parametrized classes - java

Consider I have class named A, class B extends A and class C extends A. I have a method
public static void listMethod(List<? extends A> list){
}
To this method I can pass any List of A subclasses and A class itself. But if I introduce next class:
class Holder<T>{
private final T val;
public Holder(T val){
this.val = val;
}
public T getVal() {
return val;
}
}
And also change my method to:
public static void listMethod(List<Holder<? extends A>> list){
}
I can't pass to this method any of List<Holder<A>>, List<Holder<B>> or List<Holder<C>>
Why does this happen, and how do I change my method or my classes so I will be able to pass List of Holders

I figured it out. As pecs says ? must extend type that your list will return. So next method works fine:
public static void listMethod(List<? extends Holder<? extends A>> list){
}

Related

Java class information is lost when extend class with more generics

This seems to be a compiler issue, or maybe this is there by design.
ClassA is a class with two generics. ClassB will extend ClassA by providing one solid generic type, but still expose another one.
In the following example, E will be passed in type that will extend ClassA, so when any method is called, then returned type will still be the subtype which enables to call the subtype method if needed. The motivation behinds this is to do a builder pattern, e.g.
ClassB b = new ClassB<String>().m1().m2().m3().m4()......
public class ClassA<E, T> {
public final E e;
public final T t;
public ClassA(T t) {
this.e = (E)this;
this.t = t;
}
public E printA() {
System.out.println("AAAAAA");
return e;
}
}
public class ClassB<T> extends ClassA<ClassB, T> {
public ClassB(T t) {
super(t);
}
public ClassB printB() {
System.out.println("BBBBBB");
return this;
}
public static void main(String[] args) {
ClassB<String> classB = new ClassB<>("Hello World");
// classB.printA().printA().printA(); // This will fail, after the second printA() return Object type instance instead of ClassB.
System.out.println(classB.printA().printA().getClass()); // This will print "class ClassB", so the class information it still there.
((ClassB)classB.printA().printA()).printA(); // Casting the instance to ClassB again will make it work again.
}
}
The problem is that when you call the method two times, the return instance type will be lost, so it will be an Object type, and cannot call any ClassA/B method without casting it. This is super weird.
Any idea?
Your class ClassB is a generic one, but you are opting out of generics when not providing a type parameter.
And you are doing exactly that here
public class ClassB<T> extends ClassA<ClassB, T>
^^^^^^
and here
public ClassB printB()
^^^^^^
So simply change these lines to
public class ClassB<T> extends ClassA<ClassB<T>, T>
^^^
and
public ClassB<T> printB()
^^^
Then it will work.

Can a subclass in Java override a set method and make the argument type more specific?

Say I have a class
abstract class A {
ArrayList<?> l;
public void setList(ArrayList<?> l) //set the list
}
Is it possible to do something like
class B extends A {
public void setList(ArrayList<? extends Foo> l) //Set the list }
I basically would like to specify an abstract class with a parameterised field, where a class inheriting from the first class can specify the type of the field more specifically so that it must extend some other type.
Can a subclass in Java override a set method and make the argument type more specific?
No. When overriding a method the signatures (name and argument types) have to be the same after type erasure. See JLS 8.4.2 for more information.
I basically would like to specify an abstract class with a parameterised field, where a class inheriting from the first class can specify the type of the field more specifically so that it must extend some other type.
abstract class A<T> {
public abstract void setList(ArrayList<? extends T> l);
}
class B extends A<Integer> {
#Override
public void setList(ArrayList<? extends Integer> l) {
//...
};
}
Here the compiler will perform type erasure and the signatures will be identical.
You would need to make A generic:
abstract class A<T> {
abstract void setList(List<? extends T> list);
}
And then make B something like:
class B extends A<Foo> {
#Override void setList(List<? extends Foo> list) { ...}
}
It will work if you generify the base class:
abstract class A<T> {
ArrayList<T> l;
public void setList(ArrayList<T> l) {//set the list
}
}
class B<T extends Foo> extends A<T> {
#Override
public void setList(ArrayList<T> l) {//Set the list
}
}

seek for help on java generic method

I have following class structure,
abstract class AbstractA {...}
class A1 extends AbstractA {...}
class A2 extends AbstractA {...}
abstract class AbstractB<T extends AbstractA> {
public void handle(T a) { ... }
}
class B1 extends AbstractB<A1> {
public void handle(A1 a) {
super.handle(a);
...
}
}
class B2 extends AbstractB<A2> {
public void handle(A2 a) {
super.handle(a);
...
}
}
Now I want to implement a generic method that would take a list of AbstractB and related AbstractA as parameters. e.g.
Handler.<B1, A1>handle(listOfB1, A1);
Handler.<B2, A2>handle(listOfB2, A2);
and
Handler.<B1, A2>handle(listOfB1, A2);
Handler.<B2, A1>handle(listOfB2, A1);
is not allowed.
I tried
class Handler {
// public static <T extends AbstractB<K extends AbstractA>, K extends AbstractA> handle(List<T> list, K a) {
public static <T extends AbstractB<? extends AbstractA>, K extends AbstractA> handle(List<T> list, K a) {
for (T tmp : list) {
tmp.handle(a);
}
}
}
but both does not compile. Can anyone help and give me any clue? Thanks!
Change it to :
public static <T extends AbstractB<K>, K extends AbstractA> void handle(List<T> list, K a) {
for (T tmp : list) {
tmp.handle(a);
}
}
Note that your method was missing a return type (I'm assuming your intended a void return type).
However, the main issue was that the type bound of T should be extends AbstractB<K> and not extends AbstractB<? extends AbstractA>.
Consider what happens in your current definition of the static handle method.
The current signature of the static method allows this call :
List<B1> listOfB1;
Handler.<B1, A2>handle(listOfB1, A2);
But in the body of the static method, you can't pass an A2 instance to the handle method of a B1 instance, which is why your code doesn't pass compilation. Therefore the type bound of T must depend on K.

What method signature is appropiate returning a generic object?

What should be the signature of a method that takes a generic object and returns another generic object, one that either is the same or a sub class of the original class? That is, if the method takes some generic class A, the returned object is guaranteed to be either A or B such that B extends A (directly or indirectly)?
The code below exemplifies what I'm trying to do, in the function getList():
package com.company;
import java.util.ArrayList;
public class Main {
private Main(){
List<String> stringList = new GenericMessageListCreator.getList(StringGenericMessage.class);
}
private class GenericMessageListCreator() {
public List<GenericMessage<T1>> getList(Class<T1 extends GenericMessage> clazz) {
return new ArrayList<T1>();
}
}
private class GenericMessage<T> {
public GenericMessage(){};
private T internalValue;
public void setValue(T value) {
this.internalValue = value;
}
public void echoValue() {
System.out.println("I contain " + internalValue);
}
}
private class StringMessage extends GenericMessage<String>{}
private class IntegerMessage extends GenericMessage<Integer>{}
}
Example aside, in actuality I'm writing a registry of classes that are used for Commands in a command pattern. When I get an object by its class I want to fetch the appropriate Command and pass the object to it.
I think you are looking for this signature:
public <T1 extends GenericMessage> List<GenericMessage<T1>> getList(Class<T1> clazz) {
return new ArrayList<T1>();
}
You'll find more info about generic methods here.
EDIT
Based on what I understand from your sample code, I would go for something like (I corrected some syntax errors in your code):
private class GenericMessageListCreator {
public <U, V extends GenericMessage<U>> List<U> getList(Class<V> clazz){
return new ArrayList<U>();
}
}
private class GenericMessage<T> {
public GenericMessage(){};
private T internalValue;
public void setValue(T value)
{
this.internalValue = value;
}
public void echoValue() {
System.out.println("I contain " + internalValue);
}
}
private class StringMessage extends GenericMessage<String>{}
private class IntegerMessage extends GenericMessage<Integer>{}
Thus, you'll be able to create a List<String from `StringMessage like this:
List<String> stringList = new GenericMessageListCreator().getList(StringMessage.class);
I'm not even sure which method you want to have this behavious on, but I've assuming it's getList():
private class GenericMessageListCreator() {
public <T extends GenericMessage<?>> List<T> getList(Class<T> clazz) {
return new ArrayList<T>();
}
}

Java - Generic Static Methods

I have been trying to understand whether it is possible to make a method which infers a generic type based on the return class and calls a static method of that generic type.
i.e. Below I create 2 classes both of which implement the getInstances and getAllInstances methods. I then attempt to create use the methods from a generic wrapper. It appears that the super class method is always being run regardless of the return type.
For example,
public class ParentClass {
public ParentClass(){}
public static <T extends ParentClass> T getInstance(){
return (T) new ParentClass();
}
public static <T extends ParentClass> List<T> getAllInstances(){
ArrayList<ParentClass> parents = new ArrayList<ParentClass>();
for(int i=0;i<5;i++){
parents.add(new ParentClass());
}
return (List<T>) parents;
}
}
SubclassA
public class SubclassA extends ParentClass{
public SubclassA(){}
#SuppressWarnings("unchecked")
public static SubclassA getInstance(){
return new SubclassA();
}
#SuppressWarnings("unchecked")
public static List<SubclassA> getAllInstances(){
ArrayList<SubclassA> parents = new ArrayList<SubclassA>();
for(int i=0;i<5;i++){
parents.add(new SubclassA());
}
return parents;
}
}
Wrapper - Shows the problem
public class Wrapper {
public Wrapper(){
// ... some other stuff
}
public <T extends ParentClass> T getInstance(){
return T.getInstance();
}
public <T extends ParentClass> List<T> getAllInstances(){
return T.getAllInstances();
}
public static void main(String... args){
Wrapper wrapper = new Wrapper();
SubclassA subclassA = wrapper.getInstance();
ParentClass parentClass = wrapper.getInstance();
System.out.println(subclassA.getClass().getName());
System.out.println(parentClass.getClass().getName());
}
}
When running Wrapper I get the following error:
Exception in thread "main" java.lang.ClassCastException: ParentClass cannot be cast to SubclassA
at Wrapper.main(Wrapper.java:20)
Can I do this in Java?
static methods will not be override.This static methods will belongs to Class level
Your approach is incorrect, there is no notion of static Inheritance in Java as inheritance always is in context of Object level. Only member methods (non-static) can be inherited by sub classes having appropriate access modifiers.
Update:
Further to add, in your scenario Factory pattern seems more suitable than adjusting generics for getting/constructing specific class objects.
No, you can't do it like this. To make it work you can pass the Class object:
public class ParentClassUtils
public static <T extends ParentClass> T getInstance(Class<T> clazz) {
return clazz.newInstance();
}
public static <T extends ParentClass> List<T> getAllInstances(Class<T> clazz) {
List<T> list = new ArrayList<T>();
for (int i = 0; i < 5; i++) {
list.add(getInstance(clazz));
}
return list;
}
}
Also, in your example you have equivalent static methods in parent classes and subclasses. The subclass methods don't override the parent class methods, they merely hide them, and this almost certainly isn't what you want. This utility class approach above gets around this.

Categories