Is it possible to achieve this?

T

Taras_96

Hi everyone,

I'm wondering if there's a way of achieving the following using Java
generics.

* animal class contains a set of objects of type 'animal'
* dog class extends animal, and contains a set of objects of type
'dog'
* cat class extends animal, and contains a set of objects of type
'cat'
* ....

ie: the various derived classes have sets of objects of types equal to
their own derived types. Since this architecture (ie: the set) is
common to all derived classes, it should have this logic in the Base
class. One way of achieving this is:

public class Animal <T extends Animal>
{
Set<T> theSet;
}

public class Dog extends Animal<Dog>
{
}

However, I don't like this solution because it exposes implementation
through the generic parameter. What ideally I would like is for
something similar to the following, but defined at compile time:

public class Animal
{
Set<the most specific type of object pointed to by the this pointer>
theSet;
}

Thanks

Taras
 
J

John B. Matthews

Taras_96 said:
I'm wondering if there's a way of achieving the following using Java
generics.

* animal class contains a set of objects of type 'animal'
* dog class extends animal, and contains a set of objects of type
'dog'
* cat class extends animal, and contains a set of objects of type
'cat'
* ....

ie: the various derived classes have sets of objects of types equal to
their own derived types. Since this architecture (ie: the set) is
common to all derived classes, it should have this logic in the Base
class. One way of achieving this is:

public class Animal <T extends Animal>
{
Set<T> theSet;
}

public class Dog extends Animal<Dog>
{
}

However, I don't like this solution because it exposes implementation
through the generic parameter. What ideally I would like is for
something similar to the following, but defined at compile time:

public class Animal
{
Set<the most specific type of object pointed to by the this pointer>
theSet;
}

You might look at the typesafe heterogeneous container pattern,
described by Joshua Bloch in chapter 5, item 29 of his book, _Effective
Java_:

<http://java.sun.com/docs/books/effective/>

It uses a class literal as a type token to parameterize the key instead
of the container.
 
T

Tom Anderson

I've wondered about just this myself at times. I haven't been able to come
up with a way to do it. I strongly suspect it's impossible: java's
generics are about parameterising types, and types are things that are
attached to variables, not classes or objects, so for there to be a type
binding, the type variable has to be visible at the level of variables
holding references to instances of the class. That means you can't hide
it.

In effect, there has to be a chain of type-bound references from some code
where the type is bound at compile-time to everywhere the relevant type
variable is used. For instance, in the case of a LinkedList, you go from
some client code which binds the type to List<Foo>, into the
LinkedList<Foo> itself, through into the LinkedList.Node<Foo>. There can't
be break in the chain between the source of the binding and the classes to
which it flows. I don't know if this idea of 'type flow' is something
that's been described anywhere, or even holds water, but i find it helpful
in figuring out whether, and how, i can do something with generics.
You might look at the typesafe heterogeneous container pattern,
described by Joshua Bloch in chapter 5, item 29 of his book, _Effective
Java_:

<http://java.sun.com/docs/books/effective/>

It uses a class literal as a type token to parameterize the key instead
of the container.

I don't see how it helps here.

tom
 
L

Lew

Hi everyone,

I'm wondering if there's a way of achieving the following using Java
generics.

 * animal class contains a set of objects of type 'animal'
 * dog class extends animal, and contains a set of objects of type
'dog'
 * cat class extends animal, and contains a set of objects of type
'cat'
 * ....

ie: the various derived classes have sets of objects of types equal to
their own derived types. Since this architecture (ie: the set) is
common to all derived classes, it should have this logic in the Base
class. One way of achieving this is:

public class Animal <T extends Animal>
{
  Set<T> theSet;

}

public class Dog extends Animal<Dog>
{

}

However, I don't like this solution because it exposes implementation
through the generic parameter.

That does not expose implementation.
 
J

Joshua Cranmer

Taras_96 said:
However, I don't like this solution because it exposes implementation
through the generic parameter. What ideally I would like is for
something similar to the following, but defined at compile time:

The generics pattern you mentioned is actually a relatively common one.
For example, java.lang.Enum's declaration starts:
public class Enum<T extends Enum<T>>
 
T

Tom Anderson

That does not expose implementation.

It means that instead of:

Animal a;

You have to write:

Animal<? extends Animal> a;

Despite the fact that no methods in the Animal interface have type
parameters. I think that's unnecessary implementation detail leaking out.
It might not let the client learn anything about the implementation, but
it's still messy.

tom
 
M

Mark Space

Taras_96 said:
However, I don't like this solution because it exposes implementation
through the generic parameter. What ideally I would like is for
something similar to the following, but defined at compile time:

public class Animal
{
Set<the most specific type of object pointed to by the this pointer>
theSet;
}

I honestly have no idea what this example is trying to say. Could you
give a more concrete example? For example, not only how you want to
instantiate this class, but also where you are concerned about the
implementation being exposed, and why.

Also, keep it as mathematical as possible. <Long English sentences
between angle brackets> don't cut it.
 
M

Mark Space

Tom said:
It means that instead of:

Animal a;

You have to write:

Animal<? extends Animal> a;

Despite the fact that no methods in the Animal interface have type
parameters. I think that's unnecessary implementation detail leaking
out. It might not let the client learn anything about the
implementation, but it's still messy.


Thinking about this, what if you don't have to write Animal<? extends
Animal>, but *can* just write

Animal a;

as you suggest. Now suppose given the OPs example this Animal "a" takes
Dogs but not Cats. How do you know? Aren't you just pushing errors
from compile time to runtime, and isn't that always bad? You can do
what the OP is asking right now with the existing Java API:

class Animal {
Set animals;
Class type;
<T extends Animal> Animal( Class<T> c ) {
animals = Collections.checkedSortedSet( new TreeSet(), c );
type = c;
}
boolean add( Object o ) {
return animals.add( o );
}
boolean remove( Object o ) {
return animals.remove( o );
}
}

(I used raw types because, in fact, all the type checking is being done
by the programmer. You'll need to add suppress some unchecked warning
-- the checkedSortedSet is in fact doing the checking. I didn't bother
to suppress the warnings just for this quick example.)

So now Animal can only hold subclasses of itself. That constructor
can't be called unless Class<T> is within bound, and subclasses of
Animal have no choice but to call it's sole constructor.

But what about the user? The user has some object:

Animal a;

a = new Animal( Class<Mystery> );

And darned if the compiler can help out. You as a programmer can pass
"a" anywhere and you won't always know how it's been instantiated. This
"a" will accept any object for it's parameters for add() and remove(),
and won't throw errors until runtime. Is that a great idea? Do you see
the issue with using raw types, it's a step backwards? Yet that's what
I think you are asking for.

So you might feel like you're exposing "implementation" when you require
a parameterized class, but that's a good thing. Expose the interfaces,
not the classes themselves, that you want to use to parameterize Animal.
Let the implementations remain hidden, and all will be well. There's
not too many ways to boop that up.
 
J

John B. Matthews

Tom Anderson said:
[...]
You might look at the typesafe heterogeneous container pattern,
described by Joshua Bloch in chapter 5, item 29 of his book, _Effective
Java_:

<http://java.sun.com/docs/books/effective/>

It uses a class literal as a type token to parameterize the key instead
of the container.

I don't see how it helps here.

I think you're right. I mistakenly thought the OP wanted a heterogenous
Set holding Cats or Dogs in the base class, Animal. IIUC, Java generics
are invariant, so a Set<Dog> can not be a subtype of Set<Animal>.
 
T

Tom Anderson

Thinking about this, what if you don't have to write Animal<? extends
Animal>, but *can* just write

Animal a;

as you suggest. Now suppose given the OPs example this Animal "a" takes Dogs
but not Cats. How do you know?

Because then a instanceof Dog. I think you may have misunderstood what he
was trying to do (well, or i have, but of course i think not!). His
thought process started with a situation like this:

public class Animal {
}

public class Dog extends Animal {
private Set<Dog> offspring;
}

public class Cat extends Animal {
private Set<Cat> offspring;
}

He sees the duplication in the definition of offspring, and wants to hoist
it to the base class. The obvious thing:

public class Animal {
private Set<Animal> offspring;
}

Is no good, because it lets a Dog have Cats as offspring, which as a
biologist i can assure you is nonsense. So, he comes up with:

public class Animal<T extends Animal> {
private Set<T> offspring;
}

public class Dog extends Animal<Dog> {
}

public class Cat extends Animal<Cat> {
}

And that works. As long as you're dealing with a specific kind of animal,
it's even perfectly convenient:

Cat cat = getNeighboursCat();

But as soon as you want to talk in generalities, it gets a bit messy:

Animal<?> animal;
if (neighbourHasACat) animal = getNeighboursCat();
else animal = getStrayDog();

That <?> is annoying, and doesn't really help.

tom
 
T

Taras_96

I honestly have no idea what this example is trying to say.  Could you
give a more concrete example?  For example, not only how you want to
instantiate this class, but also where you are concerned about the
implementation being exposed, and why.

Also, keep it as mathematical as possible.  <Long English sentences
between angle brackets> don't cut it.

I thought that the concrete examples I gave at the start (involving
Animals and Cats & Dogs) would have made things clear enough to
understand the generic case. Tom Anderson's message got it pretty much
spot on, let me know if it still doesn't make sense.
 
T

Taras_96

Because then a instanceof Dog. I think you may have misunderstood what he
was trying to do (well, or i have, but of course i think not!). His
thought process started with a situation like this:

public class Animal {

}

public class Dog extends Animal {
        private Set<Dog> offspring;

}

public class Cat extends Animal {
        private Set<Cat> offspring;

}

He sees the duplication in the definition of offspring, and wants to hoist
it to the base class. The obvious thing:

public class Animal {
        private Set<Animal> offspring;

}

Is no good, because it lets a Dog have Cats as offspring, which as a
biologist i can assure you is nonsense. So, he comes up with:

public class Animal<T extends Animal> {
        private Set<T> offspring;

}

public class Dog extends Animal<Dog> {

}

public class Cat extends Animal<Cat> {

}

And that works. As long as you're dealing with a specific kind of animal,
it's even perfectly convenient:

Cat cat = getNeighboursCat();

But as soon as you want to talk in generalities, it gets a bit messy:

Animal<?> animal;
if (neighbourHasACat) animal = getNeighboursCat();
else animal = getStrayDog();

That <?> is annoying, and doesn't really help.

tom

Hi Tom,

You were correct in your interpretation of my thought process... why
would you say your code at the end of your message is messy? It looks
fine to me :)

Taras
 
T

Taras_96

It means that instead of:

Animal a;

You have to write:

Animal<? extends Animal> a;

Despite the fact that no methods in the Animal interface have type
parameters. I think that's unnecessary implementation detail leaking out.
It might not let the client learn anything about the implementation, but
it's still messy.

I agree, and thus the initial question
 
L

Lew

Taras_96wrote:
Taras_96 said:
I agree, and thus the initial question

That's not implementation information, that's type information.
That's not implementation detail any more than knowing the interface
name itself is. Otherwise, why not declare the variable as 'Object
a'? Doesn't saying that 'a' is an 'Animal' "expose implementation"?

Of course not - it exposes type.

Implementation isn't what something is ('? extends Animal'), but how
it's done.

As someone mentioned upthread, this type-parameter information
prevents bugs from happening at run-time by exposing them at compile
time.

The fact that this is "[d]espite the fact that no methods in the
Animal interface [nor its subclasses] have type parameters" shows that
it doesn't reveal implementation. The type of variable 'a' in the
example has nothing to do with any of the methods.

How else will you express the requirement that the type be a subtype
of 'Animal' other than through a type parameter?

Bottom line, type analysis and its expression via generics is not
implementation. It's the whole point of having generics in the first
place.
 
M

Mark Space

Taras_96 said:
I agree, and thus the initial question


I disagree, I think you've misunderstood generics. It's almost always a
mistake to define a variable with a wildcard. Wildcards are almost
always used in method parameters, not variables.

Example:

class Animal<T extends Animal> {
Set<T> animals = new HashSet<T>();
public boolean add( T animal ) {
return animals.add( animal );
}
}
class Dog extends Animal<Dog> {}
class Cat extends Animal<Cat> {}

Animal<Animal> a = new Animal<Animal>();
a.add( new Dog() );
a.add( new Cat() );

works fine. The Cat and Dog class make ordinary objects, they still
participate in polymorphism. They are types of Animals, which is the
type of "a"'s type parameter, so they can be added to the set with out
problems.

Generics prevents you from doing something like this

Animal<Animal> a2 = new Animal<Dog>();

Because you can put Cats in a2 (as the previous example shows) but you
can't put Cats in the right-hand object (it's for Dogs only).


So basically this whole thing is a syntax question. You've
misunderstood what the syntax of Java generics means. At least that's
how I see it.


If you do declare something like:

Animal<? extends Animal> a3 = new Animal<Cat>();

Then you CAN'T PUT ANYTHING AT ALL into a3, not even Animals, nor Cats.

a3.add( new Dog() ); // FAILS
a3.add( new Cat() ); // FAILS
a3.add( new Animal<Animal>() ); // FAILS

That's why it's normally a mistake to use a wildcard when defining a
variable like a3: can't do nutin' with 'em.
 
M

Mark Space

Taras_96 said:
I thought that the concrete examples I gave at the start (involving
Animals and Cats & Dogs) would have made things clear enough to
understand the generic case. Tom Anderson's message got it pretty much
spot on, let me know if it still doesn't make sense.


See my other response just before this one. One of us is confuzzled as
all heck. I'm still not 100% sure I know who it is.
 
M

Mark Space

You were correct in your interpretation of my thought process... why
would you say your code at the end of your message is messy? It looks
fine to me :)


I just re-read this last bit. What Tom are basically doing here is

Animal<?> animal;
Object o = animal.get();
if( o instanceof Cat ) {
Cat c = (Cat) o; // neighbor's cat
}
else {
Dog d = (Dog) o; // stray dog
}

which is VERY very messy indeed. It's a huge anti-pattern in OO design
and programming, in fact.

In this situation, you want to encapsulate the behavior of an unknown
animal in the super class, then force the implementor classes to
implement the behavior themselves.

abstract class Animal {
abstract void processUnkownAnimal();
}
class Cat {
void processUnkonwAnimal() {
System.out.println("back to neighbor");
}
}
class Dog {
void processUnkonwAnimal() {
System.out.println("call the pound");
}

Then the code above becomes:

Animal<?> animal;
Object o = animal.get();
if( o instanceof Animal ) {
Animal a = (Animal) o;
a.processUnkownAnimal();
}

Or better yet:

Animal<? extends Animal> animal;
Animal a = animal.get();
a.processUnkownAnimal();



All this code untested, btw....
 
B

blue indigo

abstract class Animal {
abstract void processUnkownAnimal();
}
class Cat {
void processUnkonwAnimal() {
System.out.println("back to neighbor");
}
}
class Dog {
void processUnkonwAnimal() {
System.out.println("call the pound");
}

All this code untested, btw....

And it shows.

Error: class Cat must implement method processUnkownAnimal() from Animal
or be declared abstract.
Error: class Dog must implement method processUnkownAnimal() from Animal
or be declared abstract.
Syntax error near "}".

I count at least six typos -- four omissions and two transpositions.

Maybe when you get used to relying on your IDE to catch these kinds of
things and immediately alert you, the lack of red wavy underlines in your
newsreader starts to give you a false sense of security? :)
 
T

Taras_96

Yes, probably.  I did note in my post that the code was untested ... by
which I meant typed directly into the newsreader.

Does it really detract that much from what I'm trying to say?  It's
pretty basic what I'm doing here, I think....

Agreed, pseudo-java code is good enough to describe a pattern.

Mark, your message higlighting that the resulting code was essentially
a switch statement clearly showed the problem... you should use OO in
polymorphism
 
T

Taras_96

Mark, your message higlighting that the resulting code was essentially
a switch statement clearly showed the problem... you should use OO in
polymorphism

Of course I meant polymorphism in OO.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top