-1

I would be glad for feedback from (potential, as of the time of this writing) downvoters.

Let's assume I want to create a class storing a single object of class XXX and all its derived classes.
Should I go with LSP approach or with generics-with-bounded-type-parameter approach?
It seems to me that generic classes with a single bounded type parameters are not necessary, taking into account that Lieskov Substitution Principle (LSP) holds: Both my below classes accept objects of the same types (only as an example I use class Object and its derived classes below).

class LSP { //this class uses Lieskov Substitution Principle (LSP)
    Object a;
    LSP(Object a) { this.a= a; }
    Object get() { return this.a; }
}
class Gen<V  extends Object> { //this is a generic class with bounded type parameter 
    V a;
    Gen(V a) { this.a= a; }
    V get() { return this.a; }
}
class Stackoverflow {
    public static void main(String[] arg){
        LSP l = new LSP(5);
        Gen<Integer> g = new Gen<Integer>(5);
    }
}

Can you point me to possible differences? Advantages? Pitfalls?

Artur Opalinski
  • 1,052
  • 7
  • 12
  • 1
    When should they be used? Er... when you need a single type parameter. Also, [`class Gen` would be equivalent](http://stackoverflow.com/questions/2274720/does-extends-object-have-a-purpose-or-is-it-redundant). – Andy Turner May 20 '16 at 11:23
  • Related http://stackoverflow.com/questions/8055389/whats-the-difference-between-and-extends-object-in-java-generics – Tunaki May 20 '16 at 11:25
  • @Kevin Esche: Who should not accept what? – Artur Opalinski May 20 '16 at 11:26
  • @ArturOpalinski missread the question, deleted the comment – SomeJavaGuy May 20 '16 at 11:26
  • @Andy Turner: why not to relay on LSP then? Could you explain it in an answer? – Artur Opalinski May 20 '16 at 11:27
  • @Kevin Esche: It seems I can not remove my comment now, sorry. But I will be glad for your help and clarification. – Artur Opalinski May 20 '16 at 11:29
  • There is no relationship between these classes, so there you can't substitute one for the other (at least in a way that treats them as anything other than `Object`). – Andy Turner May 20 '16 at 11:30
  • @Andy Turner: I do not intend to substitute LSP for Gen, or vice versa. For a while let's assume I want to create a class accepting `Integer` and all derived classes. Should I go with LSP approach or with generics-with-bounded-type-parameter approach? What are the consequences of selecting one of them? – Artur Opalinski May 20 '16 at 11:46
  • @ArturOpalinski your question is basically asking why generics should be used at all. [Read the Oracle tutorial](https://docs.oracle.com/javase/tutorial/java/generics/index.html), it explains things with examples very similar to this. – Andy Turner May 20 '16 at 11:49
  • @Andy Turner: No, I am not asking about rationale to use **generics at all**. I see their rationale when type parameter is **not bounded**. I see their rationale when there is **more than one parameter**. See my question - I only do not understand the rationale to use **single type parameter** which is **bounded**. – Artur Opalinski May 20 '16 at 11:54
  • @ArturOpalinski generics are always bounded; it's just that the omission of `extends Object` is allowed. – Andy Turner May 20 '16 at 11:56
  • This question is well formed, Why people downvoting – johnny 5 May 20 '16 at 14:21
  • Posted a summary answer, and requesting your comments. After incorporating them (if any) I will 'accept' the answer in some 16 hrs from now. – Artur Opalinski May 24 '16 at 14:46

3 Answers3

3

Let's say you've got a class like this:

class SomeClass<T extends Number> {
  T instance;
  SomeClass(T instance) { this.instance = instance; }
  T get() { return instance; }
  int intValue() { return instance.intValue(); }
}

In order to invoke the instance.intValue() method, T must be of a type which has an intValue method. By constraining T to extend Number, we guarantee this.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • But ```T``` would get replaced by the bound. So in this case ```T``` would become ```Number```, and you could still call ```intValue```. – Jorn Vernee May 20 '16 at 12:11
  • @Andy Turner: I appreciate your effort and the time you devoted to me. While your example does not answer my question (why/when to chose to generics over LSP), I still had the chance to think about your comments. Jorn Vernee has shed more light in his answer. I hope I have got the difference. Thanks a lot to everybody who participated. – Artur Opalinski May 21 '16 at 05:57
1

With your example I could do:

LSP l = new LSP(5);
Integer i = (Integer) l.get();

The cast is needed, but it isn't a safe cast. I could do this as well:

LSP l = new LSP(new Object());
Integer i = (Integer) l.get(); // Crash

With generics, provided I don't use raw types, I can always be sure that I get at least an integer from get:

Gen<Integer> g = new Gen<>(new Object()); // Does not compile
...
Gen<Integer> g2 = new Gen<>(5);
Integer i = g2.get(); // no need to cast
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
0

Use comments to help me improve this summary answer to my own question, in case you see errors or incompleteness of this. Thanks once more to everyone who helped me to get this straight.

Assuming a Base class:

class Base {}

and going to use its derived classes, e.g.:

class Der1 extends Base {}
class Der2 extends Base {}
Der1 d1 = new Der1();
Der2 d2 = new Der2();

...one has the option of accepting both Der1 and Der2 instances into other classes by using either generics, or by utilizing the fact that both have the same Base parent (=utilizing Lieskov Substitution Principle, LSP):

class Gen<T extends Base> { //this class uses bounded generics
    T o;
    void set(T o){ this.o = o; }
    T get(){ return o; }
}
class LSP{              // this class uses LSP
    Base o;
    void set(Base o){ this.o = o; }
    Base get(){ return o; }
}

1. There is little difference between LSP and Gen when instantiating them for the Base class, i.e. like:

Gen<Base> g = new Gen<Base>();
LSP l = new LSP();

...because both classes are quite similar in accepting Der1 and Der2:

g.set(d1);
g.set(d2);
l.set(d1);
l.set(d2);

...and both classes have the peculiarity of casting, which is unsafe during runtime as it is not obvious which derived type is actually stored in LSP or Gen:

d2 = (Der2) g.get(); //results in runtime error if `Der1` has been stored in `g`
d2 = (Der2) l.get();  //results in runtime error if `Der1` has been stored in `l`

2. The generics Gen has distinct features when instantiated for the derived classes, i.e. like:

Gen<Der1> g1 = new Gen<>();
Gen<Der2> g2 = new Gen<>();

Now is the compiler able to ensure type safety both when putting data into Gen<>, as well as when getting data out of Gen<>, at the expense of having a separate Gen<> instance for each derived type:

g1.set(d1); //can only put d1 to g1
g2.set(d2); //can only put d2 to g2
d1 = g1.get(); // no casting needed, only d1 can be in g1
d2 = g2.get(); // no casting needed, only d2 can be in g2
Artur Opalinski
  • 1,052
  • 7
  • 12