Implement generic method with List parameter - java

I'm trying to define and then implement an abstract setter which takes a List of Objects as a parameter. Here's the gist of this simple idea:
public abstract class MyClass {
public abstract void setThings(List<?> things);
}
public class Bar extends MyClass {
private List<String> things;
#Override
public void setThings(List<String> things) {
this.things = things;
}
}
That doesn't work. I get Method does not override method from its superclass and both methods have the same erasure, but neither overrides the other. I understand the latter error relating to erasures, but even so I can't figure out the correct way to do this. I've tried some others like:
public abstract <T> void setThings(List<T> things);
...as well as a few others. And I've found other questions/answers on SO that come close to addressing this, but none that have provided a solid answer (at least not that was clear to me). I've read through the tutorials as well to no avail. What am I missing?

So Java is quite correctly telling you that you haven't implemented the abstract method setThings which takes a List<?> not a List<T> or List<String>. All of these are different things. See this question for a detailed explanation.
The simplest solution is to introduce a generic for your abstract class as well:
public abstract class MyClass<T> {
public abstract void setThings(List<T> things);
}
public class SubClass extends MyClass<String> {
private List<String> things;
public void setThings(List<String> things) {
this.things = things;
}
}

List<?> things is a list of unknown type.
List<T> things is a list of type T.
These two things aren't the same, which is why you're getting that compilation error.
There are a couple of sensible ways to eradicate the error:
Generify both classes
abstract class MyClass<T> {
public abstract void setThings(List<T> things);
}
class Bar<T> extends MyClass<T> {
private List<T> things;
#Override
public void setThings(List<T> things) {
this.things = things;
}
}
Make Bar accept a list of unknown type as well
abstract class MyClass {
public abstract void setThings(List<?> things);
}
class Bar extends MyClass {
private List<?> things;
#Override
public void setThings(List<?> things) {
this.things = things;
}
}

Related

Java Generics issue with using a parameterized class

All! I have breaking my head over this things for a few hours now. I'm sorry if it's something so trivial but I guess I don't understand Java generics well enough. I'm a novice Java programmer.
I have 2 interfaces. Int1 and Int2. Int2 extends Int1. Int2Impl implements Int2. Lesson1.java and AnotherClass.java are given below also. Questions follow after the classes.
Int1.java
public interface Int1<E> {
public Lesson1<E> interfaceimpl(Class<E> klass);
}
Int2.java
public interface Int2<E> extends Int1<E> {
String getSomething();
}
Lesson1.java
public class Lesson1<E> {
}
Int2Impl.java
public class Int2Impl<E> implements Int2<E> {
Class<E> klass;
#Override
public String getSomething() {
return "nothing";
}
#Override
public Lesson1<E> interfaceimpl(Class<E> klass) {
this.klass = klass;
return null;
}
}
AnotherClass.java
public class AnotherClass<E> {
private Int2<E> interface2;
private <E> void newMethod(Class<E> klass) {
interface2 = new Int2Impl<>();
**interface2.interfaceimpl(klass);**
}
}
The line of code that's causing a compilation issue is,
interface2.interfaceimpl(klass); in the class AnotherClass.java
the errors and the quickfixes that Eclipse offers are:
Error:
The method interfaceimpl(java.lang.Class<E>) in the type Int1<E> is not
applicable for the arguments (java.lang.Class<E>)
Quick Fixes:
1) Change method interfaceImpl(Class<E>) to interface(Class<E>)
2) Cast Argument klass to Class<E>
3) Change type of klass to Class<E>
4) Create method interfaceImpl(Class<E>) in type 'Int2'
None of the quick fixes make sense to me. Plus they also don't fix the problem regardless of which one I choose. Can someone
point out the mistake and why Eclipse throws this error?
Thanks!
Your AnotherClass is already of generic type E. No need to define E again at method level.
Just remove <E> from your newMethod() as follows:
public class AnotherClass<E> {
private Int2<E> interface2;
private void newMethod(Class<E> klass) {
interface2 = new Int2Impl<>();
interface2.interfaceimpl(klass);
}
}

The method add(T) in the type List<T> is not applicable for the arguments

I searched stack overflow for this error, but none quite had the same design as I have. Suggestions for terminology to aid in finding a similar topic like the sample code below would be appreciated.
Here is a simple test case that demonstrates the error:
import java.util.List;
public class SimpleTest {
abstract class AbsTask<T>
{
}
abstract class AbsQueue<T extends AbsTask<?>>
{
private List<T> lst;
public void addSpecialItem()
{
lst.add(new SpecialItem()); // Error occurs here
}
}
class SpecialItem extends AbsTask<Void>
{
}
}
I am trying to add a method to my abstract class AbsQueue called addSpecialItem, which will insert the SpecialItem class into the list generic list T which is essentially a list of AbsTask.
Here is the error: The method add(T) in the type List<T> is not applicable for the arguments (SimpleTest.SpecialItem)
I can resolve this error if I type case the add line as follows:
lst.add((T)new SpecialItem());
Is there a way of handling this without type casting new SpecialItem() to T?
Your abstract class must be instantiated to define what T is. Try this:
public class SimpleTest {
static abstract class AbsTask<T> { }
static class AbsQueue<T extends AbsTask<?>> {
private List<T> lst;
public void addSpecialItem(T item) {
lst.add(item);
}
}
static class Test {
public void main() {
AbsQueue<SpecialItem> queue = new AbsQueue<SpecialItem>();
queue.addSpecialItem(new SpecialItem());
}
}
static class SpecialItem extends AbsTask<String> {
}
}
A List<T> is supposed to be a list that can only include elements of type T, but the code you've written doesn't ensure that SpecialItem is a subtype of T.
It's not clear what you actually want, but I think what you want is a List<AbsTask<?>>, not a List<T> for some specific T that extends AbsTask<?>.
At that line of code lst.add(new SpecialItem()); the compiler does not yet know what T is.

Java generics and raw type

When I write the method this way. I get this warning:
BaseEvent is a raw type. References to generic type BaseEvent
should be parameterized
#Override
public <T extends BaseEvent> void actionPerformed(T event) { ... }
The code still runs fine, although the warning sign is annoying. When I write the code this way the warning goes away.
#Override
public <T> void actionPerformed(BaseEvent<T> event) { ... }
With the previous message, It doesn't guarantee that is a subClass of BaseEvent. So I changed it again:
#Override
public <T extends EventObject> void actionPerformed(BaseEvent<T> event) { ... }
#Override
public <T extends BaseEvent<T>> void actionPerformed(BaseEvent<T> event) { ... }
BaseEvent class is a class I made that extends EventOBject
public abstract class BaseEvent<T> extends EventObject
{
private String eventType;
// Constructor
public BaseEvent(Object source, String type)
{
super(source);
eventType = type;
}
public String getEventType() { return eventType; }
}
All the methods seem to work fine. But I was wondering which is the better solution.
Where do you use T in BaseEvent definition? Define it in the following way
public abstract class BaseEvent extends EventObject
then you won't get a warning with
#Override
public void actionPerformed(BaseEvent event) { ... }
UPDATE
Suppose your BaseEvent really required to be parametrized. Then write following
#Override
public <T> void actionPerformed(BaseEvent<T> event) { ... }
This will give you a parametrized method.
UPDATE 1
It doesn't guarantee that is a subClass of BaseEvent.
It does. <T> is a parameter for method template. This parameter goes to BaseEvent<T> which is subclass of EventObject by definition.
UPDATE 2
Do not use generics at the beginning of your learning. Generics are just for additional self testing. Use raw types. Then when you start to feel them, you will parametrize them correctly.
The type parameter T is never used in the class definition. You might be able to remove the type parameter from BaseEvent:
public abstract class BaseEvent extends EventObject { ... }
and just define your method without a type parameter:
#Override
public void actionPerformed(BaseEvent event) { ... }
The best solution is the one that avoids the warnings and guarantees your type safety at compile time.
If the type of T in BaseEvent doesn't matter, couldn't you just use your first one and parameterize BaseEvent? Do something like:
#Override
public <T extends BaseEvent<?>> void actionPerformed(T event) { ... }
Alternatively, it looks like your BaseEvent class does not actually use T for anything - why is it there?

Returning an objects subclass with generics

With an abstract class I want to define a method that returns "this" for the subclasses:
public abstract class Foo {
...
public <T extends Foo> T eat(String eatCake) {
...
return this;
}
}
public class CakeEater extends Foo {}
I want to be able to do things like:
CakeEater phil = new CakeEater();
phil.eat("wacky cake").eat("chocolate cake").eat("banana bread");
Arguably banana bread would throw an IllegalArgumentException with the message "Not a cake!"
public abstract class Foo<T extends Foo<T>> // see ColinD's comment
{
public T eat(String eatCake)
{
return (T)this;
}
}
public class CakeEater extends Foo<CakeEater>
{
public void f(){}
}
Edit
There is no problem to require subclass behave in a certain way that's beyond what static typing can check. We do that all the time - pages and pages of plain english to specify how you write a subclass.
The other proposed solution, with covariant return type, must do the same - asking subclass implementers, in plain english, to return the type of this. That requirement cannot be specified by static typing.
The tasteful approach from the client point of view (which is usually the one you want to take) is to use covariant return types which was added to support generics, as Michael Barker points out.
The slightly less tasteful, but more tasteful that a cast is to add a getThis method:
public abstract class Foo<T extends Foo<T>> {
protected abstract T getThis();
public T eat(String eatCake) {
...
return getThis();
}
}
public class CakeEater extends Foo<CakeEater> {
#Override protected CakeEater getThis() {
return this;
}
}
I don't think you need generics Java 5 (and later) has covariant return types, e.g.:
public abstract class Foo {
...
public Foo eat(String eatCake) {
...
return this;
}
}
public class CakeEater extends Foo {
public CakeEater eat(String eatCake) {
return this;
}
}
An approach I've used before to achieve similar behaviour is to have the subclass pass its type into a constructor of the (generified) parent type. By way of disclaimer I was generating the subclasses on the fly and inheritence was a bit of a cheat to keep my code generation simple, as always my first instinct is to try to remove the extends relationship altogether.

Generic Method Type Safety

I have the concept of NodeTypes and Nodes. A NodeType is a bunch of meta-data which you can create Node instances from (a lot like the whole Class / Object relationship).
I have various NodeType implementations and various Node implementations.
In my AbstractNodeType (top level for NodeTypes) I have ab abstract createInstance() method that will, once implemented by the subclass, creates the correct Node instance:
public abstract class AbstractNodeType {
// ..
public abstract <T extends AbstractNode> T createInstance();
}
In my NodeType implementations I implement the method like this:
public class ThingType {
// ..
public Thing createInstance() {
return new Thing(/* .. */);
}
}
// FYI
public class Thing extends AbstractNode { /* .. */ }
This is all well and good, but public Thing createInstance() creates a warning about type safety. Specifically:
Type safety: The return type Thing for
createInstance() from the type
ThingType needs unchecked conversion
to conform to T from the type
AbstractNodeType
What am I doing wrong to cause such a warning?
How can I re-factor my code to fix this?
#SuppressWarnings("unchecked") is not good, I wish to fix this by coding it correctly, not ignoring the problem!
You can just replace <T extends AbstractNode> T with AbstractNode thanks to the magic of covariant returns. Java 5 added support, but it didn't receive the pub it deserved.
Two ways:
(a) Don't use generics. It's probably not necessary in this case. (Although that depends on the code you havn't shown.)
(b) Generify AbstractNodeType as follows:
public abstract class AbstractNodeType<T extends AbstractNode> {
public abstract T createInstance();
}
public class ThingType<Thing> {
public Thing createInstance() {
return new Thing(...);
}
}
Something like that should work:
interface Node{
}
interface NodeType<T extends Node>{
T createInstance();
}
class Thing implements Node{}
class ThingType implements NodeType<Thing>{
public Thing createInstance() {
return new Thing();
}
}
class UberThing extends Thing{}
class UberThingType extends ThingType{
#Override
public UberThing createInstance() {
return new UberThing();
}
}

Categories