Reputation: 857
tl;dr
Trying to implement a hierarchal fluent interface such that I can combine nodes child classes while also the class standalone, but getting type parameter is not within its bound errors.
Details
I'm trying to achieve a solution so that I can create something such that I can do something like:
farm
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
while also being able to do:
Human human = new Human()
.saysHello()
I've gotten close using various strategies but haven't been able to gain the flexibility described.
My current attempt uses the following classes:
abstract class Base<T extends Base<T>>{
private T parent;
Base(){
}
Base( T parent ){
this.parent = parent;
}
public T done() throws NullPointerException{
if ( parent != null ){
return (T) parent;
}
throw new NullPointerException();
}
}
class Farm<T extends Base<T>> extends Base{
private Animal<Farm<T>> animal;
private Human<Farm<T>> human;
public Farm(){
super();
this.animal = new Animal( this );
this.human = new Human( this );
}
public Animal<Farm> animal(){
return this.animal;
}
public Human<Farm<T>> human(){
return this.human;
}
}
class Animal <T extends Base<T>> extends Base{
private Cat<Animal<T>> cat;
private Dog<Animal<T>> dog;
public Animal(){
super();
init();
}
public Animal( T parent ){
super( parent );
init();
}
private void init(){
this.cat = new Cat(this);
this.dog = new Dog(this);
}
public Cat<Animal<T>> cat(){
return cat;
}
public Dog<Animal<T>> dog(){
return dog;
}
}
class Human<T extends Base<T>> extends Base{
public Human<T> saysHello(){
System.out.println("human says hi");
return this;
}
}
class Cat <T extends Base<T>> extends Base{
private Human<Cat> human;
public Cat(){
super();
init();
}
public Cat( T parent ){
super( parent );
init();
}
private void init(){
this.human = new Human();
}
public Cat<T> meow(){
System.out.println("cat says meow");
return this;
}
public Human<Cat<T>> findsHuman(){
return this.human;
}
}
class Dog <T extends Base<T>> extends Base{
private Human<Dog> human;
public Dog(){
super();
init();
}
public Dog( T parent ){
super( parent );
init();
}
private void init(){
this.human = new Human();
}
public Dog<T> bark(){
System.out.println("dog says woof");
return this;
}
public Dog<T> chacesCar(){
System.out.println("cat drinks milk");
return this;
}
public Human<Dog<T>> findsHuman(){
return this.human;
}
}
The errors I'm seeing are commonly:
Animal.java:4: type parameter Animal is not within its bound private Cat cat; Animal.java:5: type parameter Animal is not within its bound private Dog dog;
Applied to all similar references and also pertaining to my example desired case:
cannot find symbol symbol : method dog() location: class Base.dog()
I've tried using the following solutions which seemed to tackle similar problems, but to no avail, so any and all support is welcome.
References
Is there a way to refer to the current type with a type variable?
http://vyazelenko.com/2012/03/02/recursive-generics-to-the-rescue/
Upvotes: 9
Views: 2446
Reputation: 149075
Your problem is that the method done should return the parent, but the parent is not necessarily a T but is just a Base. And the other problem is that whatever the class is, the done
method should always return the same class.
But here is a slight variation of your proposed classes. First for Base
declaring its concrete class and its concrete parent :
abstract class Base<T extends Base<T, P>, P>{
private P parent;
Base(){
}
Base( P parent ){
this.parent = parent;
}
public P done() throws NullPointerException{
if ( parent != null ){
return parent;
}
throw new NullPointerException();
}
}
That being done, the derived concrete classes become :
class Farm extends Base<Farm, Object>{
private Animal animal;
private Human human;
public Farm(){
super();
this.animal = new Animal( this );
this.human = new Human( this );
}
public Animal animal(){
return this.animal;
}
public Human human(){
return this.human;
}
}
class Animal extends Base<Animal, Farm>{
private Cat cat;
private Dog dog;
public Animal(){
super();
init();
}
public Animal( Farm parent ){
super( parent );
init();
}
private void init(){
this.cat = new Cat(this);
this.dog = new Dog(this);
}
public Cat cat(){
return cat;
}
public Dog dog(){
return dog;
}
}
class Human extends Base<Human, Farm>{
public Human() {
}
public Human(Farm farm) {
super(farm);
}
public Human saysHello(){
System.out.println("human says hi");
return this;
}
}
class CatOrDog extends Base<Cat, Animal>{
protected Human human;
public CatOrDog(){
super();
init(null);
}
public CatOrDog( Animal parent ){
super( parent );
init(parent);
}
private void init(Animal parent){
Animal parent = done();
Farm farm = (parent == null) ? null : parent.done();
this.human = new Human(farm);
}
public Human findsHuman(){
return this.human;
}
}
class Cat extends CatOrDog{
public Cat(){
super();
}
public Cat( Animal parent ){
super( parent );
}
public Cat meow(){
System.out.println("cat says meow");
return this;
}
}
class Dog extends CatOrDog {
public Dog(){
super();
}
public Dog( Animal parent ){
super( parent );
}
public Dog bark(){
System.out.println("dog says woof");
return this;
}
public Dog chacesCar(){
System.out.println("cat drinks milk");
return this;
}
}
With that, I could write without any error or warning :
Farm farm = new Farm();
farm.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.animal()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.animal()
.done()
.human()
.saysHello()
.done();
But note that I had to replace to done
calls with animals
calls.
Edit :
I added a new class CatOrDog
to factorize the Human
processing. As the parent of a Human
is a Farm
, I initialize the new human
with a correct parent if it exists. That way, not only the above sources compiles without error or warning, but it also runs without any problem and it prints :
cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi
Upvotes: 1
Reputation: 1208
The code below seems to work fine and doesn't need any @SuppressWarnings
. The key concept to grasp is that your T
parameter is effectively the class of your object's parent, but T
's parent could be anything. So instead of T extends Base<T>
you want T extends Base<?>
.
The output is:
cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi
...which I believe is correct, although you might want to change your Dog.chacesCar()
method so it doesn't output cat drinks milk
! Also it should be chases
not chaces
.
Hope this helps!
abstract class Base<T extends Base<?>> {
private final T parent;
Base() {
this.parent = null;
}
Base(T parent) {
this.parent = parent;
}
public T done() throws NullPointerException {
if (parent != null) {
return parent;
}
throw new NullPointerException();
}
}
class Farm<T extends Base<?>> extends Base<T> {
private final Animal<Farm<T>> animal;
private final Human<Farm<T>> human;
public Farm() {
super();
this.animal = new Animal<>(this);
this.human = new Human<>(this);
}
public Animal<Farm<T>> animal() {
return this.animal;
}
public Human<Farm<T>> human() {
return this.human;
}
}
class Animal<T extends Base<?>> extends Base<T> {
private Cat<Animal<T>> cat;
private Dog<Animal<T>> dog;
public Animal() {
super();
init();
}
public Animal(T parent) {
super(parent);
init();
}
private void init() {
this.cat = new Cat<>(this);
this.dog = new Dog<>(this);
}
public Cat<Animal<T>> cat() {
return cat;
}
public Dog<Animal<T>> dog() {
return dog;
}
}
class Human<T extends Base<?>> extends Base<T> {
public Human() {
super();
}
public Human(T parent) {
super(parent);
}
public Human<T> saysHello() {
System.out.println("human says hi");
return this;
}
}
class Cat<T extends Base<?>> extends Base<T> {
private Human<Cat<T>> human;
public Cat() {
super();
init();
}
public Cat(T parent) {
super(parent);
init();
}
private void init() {
this.human = new Human<>(this);
}
public Cat<T> meow() {
System.out.println("cat says meow");
return this;
}
public Human<Cat<T>> findsHuman() {
return this.human;
}
}
class Dog<T extends Base<?>> extends Base<T> {
private Human<Dog<T>> human;
public Dog() {
super();
init();
}
public Dog(T parent) {
super(parent);
init();
}
private void init() {
this.human = new Human<>(this);
}
public Dog<T> bark() {
System.out.println("dog says woof");
return this;
}
public Dog<T> chacesCar() {
System.out.println("cat drinks milk");
return this;
}
public Human<Dog<T>> findsHuman() {
return this.human;
}
}
Test code:
public static void main(String[] args) {
Farm<?> farm = new Farm<>();
farm
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
Human human = new Human()
.saysHello();
}
Upvotes: 4
Reputation: 4872
The best thing I came up is the following:
new Animal()
.cat()
.meow()
.findsHuman()
.<Cat>done()
.<Animal>done()
.dog()
.bark()
.findHuman()
.<Dog>done()
.done();
With the following base class:
public abstract class Base<T extends Base<T>>{
private Base<?> backRef;
public Base() {}
public Base(Base<?> backRef) {
this.backRef = backRef;
}
@SuppressWarnings("unchecked")
protected T self() {
return (T)this;
}
@SuppressWarnings("unchecked")
public <U extends Base<U>> U done() {
return (U)backRef;
}
}
If you declare backRef as of Type T then the other classes are not allowed because they are not a subclasses of each other, so you have to specify a different type, but since this type is context dependent (one time its Cat, one time its Dog) I don't see an alternative as to pass a hint.
I found a solution:
new Animal()
.cat()
.meow()
.findsHuman()
.done()
.done()
.dog()
.bark()
.findHuman()
.done()
.done();
public abstract class Base<T extends Base<T,P>, P>{
private P backRef;
public Base() {}
public Base(P backRef) {
this.backRef = backRef;
}
@SuppressWarnings("unchecked")
protected T self() {
return (T)this;
}
public P done() {
return backRef;
}
}
Like someone suggested, we add an additional Type for the parent.
Now the base classes:
public final class Cat extends Base<Cat, Animal>{
public Cat() {}
public Cat(Animal backRef) {
super(backRef);
}
public Cat meow() {
System.out.println("Meeeoooww");
return self();
}
public Human<Cat> findsHuman() {
return new Human<Cat>(this);
}
}
As you can see, Cat clearly specifies which base type it should use. Now for human, which can change the type depending on the context:
public final class Human<P> extends Base<Human<P>, P> {
public Human() {}
public Human(P backRef) {
super(backRef);
}
}
Human specifies an additional generic which the caller (Cat, Dog) specifies in their findHuman() Method.
Upvotes: 3
Reputation: 48837
You could also play with interfaces, so that you can fake multiple inheritance. A bit verbose, but there is no hazardous casting, and I find it quite understandable.
Define the available methods:
public interface AnimalIn {
AnimalOut animal();
}
public interface CatIn {
CatOut cat();
}
public interface MeowIn {
CatOut meow();
}
public interface DogIn {
DogOut dog();
}
public interface BarkIn {
DogOut bark();
}
public interface ChacesCarIn {
DogOut chacesCar();
}
public interface FindsHumanIn<T> {
HumanOut<T> findsHuman();
}
public interface HumanIn {
HumanOut<FarmOut> human();
}
public interface SaysHelloIn<T> {
HumanOut<T> saysHello();
}
public interface DoneIn<T> {
T done();
}
You may need to have multiple methods in an interface, but I haven't met this need yet. For example, if you had had to kinds of meow
s:
public interface MeowIn {
CatOut meowForFood();
CatOut meowForMilk();
CatOut meowForStrokes();
}
Define the output types:
Farm
provides Animal
or Human
:
public interface FarmOut extends AnimalIn, HumanIn {
// no specific methods
}
Animal
provides Cat
, Dog
or Done
:
public interface AnimalOut extends CatIn, DogIn, DoneIn<FarmOut> {
// no specific methods
}
Cat
provides Meow
, FindsHuman
or Done
:
public interface CatOut extends MeowIn, FindsHumanIn<CatOut>, DoneIn<AnimalOut> {
// no specific methods
}
Dog
provides Bark
, ChacesCar
, FindsHuman
or Done
:
public interface DogOut extends BarkIn, ChacesCarIn, FindsHumanIn<DogOut>, DoneIn<AnimalOut> {
// no specific methods
}
Human
provides SayHello
or Done
:
public interface HumanOut<T> extends SaysHelloIn<T>, DoneIn<T> {
// no specific methods
}
Simply implement the *Out interfaces:
public class Farm implements FarmOut {
@Override
public AnimalOut animal() {
return new Animal(this);
}
@Override
public HumanOut<FarmOut> human() {
return new Human<FarmOut>(this);
}
}
public class Animal implements AnimalOut {
private FarmOut chain;
public Animal(FarmOut chain) {
this.chain = chain;
}
@Override
public CatOut cat() {
return new Cat(this);
}
@Override
public DogOut dog() {
return new Dog(this);
}
@Override
public FarmOut done() {
return chain;
}
}
public class Dog implements DogOut {
private AnimalOut chain;
public Dog(AnimalOut chain) {
this.chain = chain;
}
@Override
public DogOut bark() {
System.out.println("bark");
return this;
}
@Override
public DogOut chacesCar() {
System.out.println("chaces car");
return this;
}
@Override
public HumanOut<DogOut> findsHuman() {
return new Human<DogOut>(this);
}
@Override
public AnimalOut done() {
return chain;
}
}
public class Cat implements CatOut {
private AnimalOut chain;
public Cat(AnimalOut chain) {
this.chain = chain;
}
@Override
public CatOut meow() {
System.out.println("meow");
return this;
}
@Override
public HumanOut<CatOut> findsHuman() {
return new Human<CatOut>(this);
}
@Override
public AnimalOut done() {
return chain;
}
}
public class Human<T> implements HumanOut<T> {
private T chain;
public Human(T chain) {
this.chain = chain;
}
@Override
public HumanOut<T> saysHello() {
System.out.println("hello");
return this;
}
@Override
public T done() {
return chain;
}
}
Those implementations would work also without the interfaces: remove the implements *Out
, the @Override
s, and replace any *Out
by *
(e.g. AnimalOut
by Animal
). That said, it's easier to maintain with the interfaces: simply update them and fix your compilation errors. It's also easier to find DSL solutions with interfaces (as you can see), and they are sometimes simply necessary.
Demo:
new Farm()
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
Prints:
meow
hello
bark
chaces car
hello
hello
Upvotes: 1
Reputation: 18204
This is what we did on one our project:
public abstract class Parent<T extends Parent<T>> {
/**
* Get {@code this} casted to its subclass.
*/
@SuppressWarnings("unchecked")
protected final T self() {
return (T) this;
}
public T foo() {
// ... some logic
return self();
}
// ... other parent methods
}
public class Child extends Parent<Child> {
public Child bar() {
// ... some logic
return self();
}
// ... other child methods
}
Allowing child to have its own subclass would be:
public class Child<T extends Child<T>> extends Parent<T> {
public T bar() {
// ... some logic
return self();
}
}
Upvotes: 2
Reputation: 299048
There is no "safe" way to do this, but this should compile:
class Dog extends Base{
<T extends Dog> T bark(){
return (T) this;
}
}
Upvotes: 0
Reputation: 2921
In this line:
class Farm<T extends Base<T>>
The compiler treats the second type parameter as a concrete class. For example, say if you replaced that line with this:
class Farm<T extends Base<Double>>
'Double' is a concrete class. When the compiler scans this, it cannot tell the difference between your T and Double, and such treats them both as concrete class, and not type parameters. The only way to let the compiler know T is a type parameter is this way:
class Farm<T extends Base<T>, T>
I hope this answers (or is at least relevant) to your question.
edit Post was edited while I was typing, so I guess this answer isn't relevant anymore.
Upvotes: 1