Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what the usefulness about Java generics involving inheritance and generics extends self

Tags:

java

generics

I find the generics whose generics params extends itself(here). I don't understand that well.I suspect that it is wrong at the beginning, but no one put forward. I have some questions about this:

  1. How to use the Variant generics, can you give me a example?
  2. What the benefit or effect of this generics style.

here is the generics style code which is picked from (here).

abstract class Base<T extends Base<T>> {

}

class Variant<T extends Variant<T>> extends Base<T> {

}

Thanks!

like image 960
xxxzhi Avatar asked Oct 17 '15 12:10

xxxzhi


2 Answers

I think you're talking about F-bounded types. I've found them very useful in situations where two hierarchies are directly correlated. The most clear case that comes to mind is the builder pattern, targeted to a hierarchy of classes. In this case, it could be of use to have a hierarchy of builders as well.

An example might shed some light. Consider the following (very stereotyped) hierarchy:

public abstract class Human {

    protected String name;

    protected int age;
}

public class Woman extends Human {

    protected String loveMovie;
}

public class Man extends Human {

    protected String soccerTeam;
}

Now, we want to create builders for Man and Woman. We could implement a builder for each one, duplicating the methods to set name and age attributes. However, as Man and Woman inherit from Human, we could have an abstract HumanBuilder, and make our WomanBuilder and ManBuilder inherit from it. This is where F-bounded types come in handy.

The Human class, along with its HumanBuilder, would be as follows:

public abstract class Human {

    protected String name;

    protected int age;

    public static abstract class HumanBuilder<H extends Human, 
                                              T extends HumanBuilder<H, T>> {
        protected String name;

        protected int age;

        @SuppressWarnings("unchecked")
        public T name(String name) {
            this.name = name;
            return (T) this;
        }

        @SuppressWarnings("unchecked")
        public T age(int age) {
            this.age = age;
            return (T) this;
        }

        protected void fill(H human) {
            human.name = this.name;
            human.age = this.age;
        }

        protected abstract H create();

        public final H build() {
            H human = this.create();
            this.fill(human);
            return human;
        }
    }
}

This would be the Woman class, along with its WomanBuilder:

public class Woman extends Human {

    protected String loveMovie;

    public static class WomanBuilder extends HumanBuilder<Woman, WomanBuilder> {

        protected String loveMovie;

        public WomanBuilder loveMovie(String loveMovie) {
            this.loveMovie = loveMovie;
            return this;
        }

        @Override
        protected void fill(Woman woman) {
            super.fill(woman);
            woman.loveMovie = this.loveMovie;
        }

        @Override
        protected Woman create() {
            return new Woman();
        }
    }
}

And finally, here's the Man class, along with its ManBuilder:

public class Man extends Human {

    protected String soccerTeam;

    public static class ManBuilder extends HumanBuilder<Man, ManBuilder> {

        protected String soccerTeam;

        public ManBuilder soccerTeam(String soccerTeam) {
            this.soccerTeam = soccerTeam;
            return this;
        }

        @Override
        protected void fill(Man man) {
            super.fill(man);
            man.soccerTeam = this.soccerTeam;
        }

        @Override
        protected Man create() {
            return new Man();
        }
    }
}

This approach saves quite a lot of lines of code, especially in a real-world use case.

As expected, using the builders does not require any casting:

Man man = new Man.ManBuilder()
    .name("Joe")
    .age(29)
    .soccerTeam("Los Angeles Galaxy")
    .build();

Woman woman = new Woman.WomanBuilder()
    .name("Jane")
    .age(25)
    .loveMovie("Meet Joe Black")
    .build();
like image 112
fps Avatar answered Sep 28 '22 05:09

fps


From the code you linked, it looks like the Base and Variant classes hold methods that return references to an object of their own class, I'm guessing similar to a Singleton.

abstract class Base {
    protected abstract Base getNewInstance();
}

Now if you wanted to return an instance of a subclass of Base, you'd be out of luck. That's where generics come in.

class Variant<T extends Variant<T>> extends Base<T> {
    protected Base<T> getNewInstance() { 
        return new Variant(); 
    }
}

In regards to usefulness, I personally don't really see any. It's unnecessarily complicated and could probably be refactored to something much more readable.

like image 45
Luka Jacobowitz Avatar answered Sep 28 '22 05:09

Luka Jacobowitz