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.
Related
I am reading Effective Java Edition 3. in chapter 2 page 14, the author talks about the builder pattern and presents this code:
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
an implementation of above abstract class:
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
#Override public NyPizza build() {
return new NyPizza(this);
}
#Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
and we can use code like this:
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
quote from the book:
Note that Pizza.Builder is a generic type with recursive type
parameter. this, along with the abstract self method, allows method
chaining to work properly in subclasses, without the need for casts.
now my question is what power/value <T extends Builder<T>> added to Pizza class and how it is different with <T extends Builder>? if you are going to explain <T extends Builder<T>> to a five years old kid in simple English how would you explain it?
and I can't figure out the purpose of abstract self method in the super class?
I add this part because of the comment section
Imagine I have changed the above codes like this(it is not going to be the best example just for illustration purposes):
I changed the NyPizza.Builder to be generic:
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder<T> extends Pizza.Builder<Builder<T>> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
#Override public NyPizza build() {
return new NyPizza(this);
}
#Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
and the Pizza class like this:
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder> {
T obj;
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
public T builder(){
return obj;
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
and used the above classes like this:
NyPizza.Builder<String> test = new NyPizza.Builder<String>(SMALL).builder();
now because I didn't define class Builder<T extends Builder> in this form: class Builder<T extends Builder<T>> the builder method in the Pizza class should not be able to detect the type of the T is NyPizza.Builder<String> but it can. how is it possible? how change it to need casting?
Imagine a class:
class Foo<T extends Foo<T>> {
T foo() { ... }
}
What this is saying is that the foo() method, in the Foo<T> class, will return an instance of something that's also a Foo<T>, because all possible types represented by T are subclasses of Foo<T>.
So it can return itself - which is why it's useful for method chaining with builders; but there's actually nothing that stops it returning some other class within the bound.
If you were to declare it as:
class Foo<T extends Foo> {
T foo() { ... }
}
then this makes T a raw-typed Foo: foo() returns a Foo, not a Foo<T>. Because raw types erase all generics, this means that in a call chain like foo().foo(), you are losing type information after the first call.
In many situations, this may not feel super-important. But raw types should be avoided in pretty much all situations. The consequences of a raw type here would be:
If you weren't returning the self type (alluded to above with the "there's actually nothing that stops it returning some other class"), you would lose that "other class" after the first method call.
If you had other generic methods in Foo, e.g. List<String> myList() { ... }, the raw-typed Foo would return raw List, not List<String> (raw types erase all generics, not just the ones related to the omitted type variables).
These clearly don't apply in all situations; but since it is simply bad practice to introduce unnecessary raw types, make sure you don't introduce them.
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;
}
}
I come from .Net and I'm pretty new to Java development so maybe that's a weird question:
I have a class hierarchy like:
Superclass implements GenericInterface<Superclass>
^
|
Subclass
where GenericInterface is pretty straight forward:
public interface GenericInterface<T> {
OtherGenericInterface<T> getOther();
}
and OtherGenericInterface finally uses the type parameter:
public interface OtherGenericInterface<T> {
List<Object> processType(T left, T right);
}
now when I try to implement the the interface in Superclass I simply return an anonymous type:
public class Superclass implements GenericInterface<Superclass> {
#Override
public OtherGenericInterface<Superclass> getOther() {
return new OtherGenericInterface<Superclass>() {
#Override
public List<Object> processType(T left, T right) {
...
}
};
}
}
That works fine so far but now I try to override the method in the Subclass:
public class Subclass extends Superclass (implements GenericInterface<Subclass>) {
#Override
public OtherGenericInterface<Subclass> getOther() {
...
}
}
And in there, I can not override the method with my more specific return type. Even if I re-implement the interface and declare the method in the Superclass as final it is not possible.
So my question is: Why isn't OtherInterface<MoreSpecificType> a more specific, or at least the same type (due to type erasure) because that would be the requirement to override the method right?.
This demonstrates a common misconception with Java generics - believing that a match of a class will also match subclasses (like types of parameters). That is not the case. Java generics are designed to ensure that the types match exactly. If you want wriggle room you must specify and define what room you want and how much.
Here's a version that allows you to do what you want by exactly specifying the signature as <T extends Superclass>. This may not be exactly what you are looking for but I hope it points you in the right direction.
public interface OtherGenericInterface<T> {
List<Object> processType(T left, T right);
}
public interface GenericInterface<T> {
OtherGenericInterface<T> getOther();
}
public class Superclass<T extends Superclass> implements GenericInterface<T> {
#Override
public OtherGenericInterface<T> getOther() {
return new OtherGenericInterface<T>() {
#Override
public List<Object> processType(Superclass left, Superclass right) {
return null;
}
};
}
}
public class Subclass extends Superclass {
#Override
public OtherGenericInterface<Subclass> getOther() {
return null;
}
}
I'm studying Java Generic type.
I have the abstract class AbstractInputdata.
public abstract class AbstractInputData {
....
}
Some class that extend AbstractInputData
public class Email extends AbstractInputData{
...
}
public class Mobile extends AbstractInputData{
...
}
......
A.
public class ProcessorA {
public static boolean isCustomData(AbstractInputData abstractInputData) {
....
}
}
B.
public class ProcessorB {
public static <T extends AbstractInputData> boolean isCustomData(T t) {
...
}
}
Is there any difference between A and B?
The only difference is that the second method with appear as a generic typed method via Reflections. It's behaviour will be the same except in odd cases like this
processorB.<MyType>isCustomData(t); // won't compile unless t is a MyType
You would have to tell it what type you expect it to match, which isn't that useful IMHO.
Since your methods only produce a boolean, there is no difference. But in case you want to return the input you can use B to preserve the generic type:
public class ProcessorB {
public static <T extends AbstractInputData> boolean isCustomData(T t) {
...
}
public static <T extends AbstractInputData> T copyCustomData(T t) {
...
}
}
ProcessorA could only return an object of type AbstractInputData while processorB returns Email or Mobile depending on the parameter type.
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();
}
}