I've tried to condense this issue down to the smallest amount of code that I could.
I have defined a table structure, like a database table, with the Row
and Table
classes below. A Table
is essentially a list of Row
. I want subclasses to define their particular flavor of Table
and Row
and I want the compiler to catch inappropriate attempts to put rows of one type into tables of an incompatible type.
The abstract Agent
class provides a method to take parameters and return a table that takes rows of type T
. I have defined three methods to illustrate the problem that I am having.
The FinalAgent
, FinalTable
, and FinalRow
classes define implementations of the Agent
, Table
, and Row
classes. Ultimately, what I want is method2a
, which takes a list of parameters and returns a table of type FinalTable
.
public abstract class Row {}
public abstract class Table<T extends Row> {}
public abstract class Agent {
public <T extends Row> Table<T> method1(List<String> parameter) {
return null;
}
public <T extends Row> Table<T> method2a(List<String> parameter) {
return null;
}
public <T extends Row> Table<T> method2b(String parameter) {
return null;
}
}
public class FinalRow extends Row {}
public class FinalTable extends Table<FinalRow> {}
public class FinalAgent extends Agent {
@Override
public <T extends Row> Table<T> method1(List<String> parameter) {
return null;
}
@Override
public FinalTable method2a(List<String> parameter) {
return null;
}
@Override
public FinalTable method2b(String parameter) {
return null;
}
}
At the bottom:
method1
of FinalAgent
compiles, but I have to write Table<FinalRow> t1 = new FinalAgent().method1(null);
in order to call the method.method2a
of FinalAgent
I changed the return type to FinalTable
to reflect what I am actually returning (I want to write FinalTable t2a = new FinalAgent().method2a(null);
), but the compiler produces the error: The method method2a(List) of type FinalAgent must override or implement a supertype method
method3
I changed the parameter from a List
to a String
. The method compiles OK but gives me a type safety warning, which I can at least work with.So, finally, the question: Is it a compiler bug that method2a
in FinalAgent
does not compile yet method2b
does compile?
I might as well also ask, is there a better way to do what I am doing?
Core Java bootcamp program with Hands on practiceA type parameter can have multiple bounds.
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class. These are known as bounded-types in generics in Java.
A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
Multiple parameters You can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
It's not clear why you're parameterizing the methods. You're promising to return a Table<T>
, but your method can't identify T
at runtime because of type erasure. You probably want to parameterize the entire Agent
class instead:
public abstract class Agent<T extends Table<?>> {
public T method1(List<String> parameter) {
return null;
}
public T method2a(List<String> parameter) {
return null;
}
public T method2b(String parameter) {
return null;
}
}
public class FinalAgent extends Agent<FinalTable> {
@Override
public FinalTable method1(List<String> parameter) {
return null;
}
@Override
public FinalTable method2a(List<String> parameter) {
return null;
}
@Override
public FinalTable method2b(String parameter) {
return null;
}
}
As for your initial question, I can't make sense of the discrepancy or the warning message on method2b
.
Using a method parameter at a single place means the parameter is usually unnecessary
With methods like that in Agent
, FinalAgent
compiles fine:
public Table<? extends Row> method2a(List<String> parameter) {
return null;
}
public Table<? extends Row> method2b(String parameter) {
return null;
}
This is direct consequence of raw type.
method1
is still a generic method : no issue.method2b
is not anymore a generic method but override the method in Agent
: no issue (except the safety warning though, due to the unchecked conversion)method2a
is not anymore a generic method but does not override anything
method2a
is seen declared like this from FinalAgent
, after compilation :
public Table method2a(List parameter)
But defined like this in FinalAgent
:
public FinalTable method2a(List<String> parameter)
And now you are stuck. Same erasure but does not override correctly method2a
Here is a simple example :
public interface Foo
{
public void bar(List a);
}
public class FooChild implements Foo
{
@Override
public void bar(List<String> a)
{
}
}
It gives The method bar(List<String>) of type FooChild must override or implement a supertype method
public interface Foo
{
public void bar(List a);
}
public class FooChild implements Foo
{
public void bar(List<String> a)
{
}
}
It gives Name clash: The method bar(List<String>) of type FooChild has the same erasure as bar(List) of type Foo but does not override it
Another example. This code compiles fine :
public interface Foo
{
public void bar(List<String> a);
}
public class FooChild implements Foo
{
public void bar(List<String> a)
{
}
}
This one does not :
public interface Foo
{
public <T> void bar(List<String> a);
}
public class FooChild implements Foo
{
public void bar(List<String> a)
{
}
}
It gives Name clash: The method bar(List<String>) of type FooChild has the same erasure as bar(List<String>) of type Foo but does not override it
As soon as bar
becomes a generic method, it is seen with raw types and we can notice the same issue.
A different example of raw type issue : Name clash when overriding method of generic class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With