Converting Sets

R

Roedy Green

What is the cleanest way to do this conversion?

I have a Set<X>.
I want a Set<Y> where X implements Y.
 
M

Mayeul

What is the cleanest way to do this conversion?

I have a Set<X>.
I want a Set<Y> where X implements Y.

This conversion exactly as asked:

Set<X> setOfX = obtainSetOfX();
Set<Y> setOfY = new YourPreferredSetImplementation<Y>(setOfX);

or:

Set<X> setOfX = obtainSetOfX();
Set<Y> setOfY = new YourPreferredSetImplementation<Y>();
setOfY.addAll(setOfX);

Conversion of a Set<X> to a Set<? extends Y>, ensuring the Set contains
objects assignable to Y, but no new elements can be added to it:

Set<X> setOfX = obtainSetOfX();
Set<? extends Y> setOfY = setOfX;

Also, consider the possibility to build a Set<Y> and put X objects into
it, in the first place.
 
R

Roedy Green

Conversion of a Set<X> to a Set<? extends Y>, ensuring the Set contains
objects assignable to Y, but no new elements can be added to it:

Set<X> setOfX = obtainSetOfX();
Set<? extends Y> setOfY = setOfX;

This what I was looking for , that did not require an element by
element copy.
Ah, but this this you created is NOT a Set<Y>. You cannot add
arbirary Y to it, just more X. I feel queasy. How could the compiler
keep track that setOfY could only contain X.
 
M

Mayeul

This what I was looking for , that did not require an element by
element copy.
Ah, but this this you created is NOT a Set<Y>. You cannot add
arbirary Y to it, just more X. I feel queasy. How could the compiler
keep track that setOfY could only contain X.

It cannot. The compiler notices that setOfY is parametered with an
extends wildcard, and therefore does not know what exactly it can or
cannot contain.

Therefore, you cannot add anything to setOfY. But you can still add more
X to setOfX, and they point to the same object...
 
M

markspace

On 02/02/2012 14:32, Roedy Green wrote:
It cannot.


I think this is covered in Effective Java. Generics are a compile time
thing. If you want runtime, you have to roll your own (I don't know of
any classes in the API that allow you to have a runtime type parameter,
although I guess there may be some).

class MySet extends SomeSet {

Class type;

MySet( Class type ) { this.type = type; }

void add( Object o ) {
if( o instanceof type ) super.add( o );
}
}

I think is the above is the gist of how EJ handles it. Add a type token
(the "type" variable) and test for types as they are added. If you want
generics too, add them in:

class MySet<T> extends Set<T> {

Class<? extends T> type;

MySet( Class<? extends T> type ) { this.type = type; }

void add( T o ) {
if( o instanceof type ) super.add( o );
}
}

Not compiled, but I think that gives the right idea.
 
D

Daniel Pitts

This what I was looking for , that did not require an element by
element copy.
Ah, but this this you created is NOT a Set<Y>. You cannot add
arbirary Y to it, just more X. I feel queasy. How could the compiler
keep track that setOfY could only contain X.
It does not. Instead, it prevents you from any add operation which
requires the type.

In other words, setOfY becomes somewhat "read-only" for typed
operations. You can still clear it, or sub-sets of it, but you can no
longer add *any* object to it, whatever type.
 
J

Jim Janney

markspace said:
I think this is covered in Effective Java. Generics are a compile
time thing. If you want runtime, you have to roll your own (I don't
know of any classes in the API that allow you to have a runtime type
parameter, although I guess there may be some).

class MySet extends SomeSet {

Class type;

MySet( Class type ) { this.type = type; }

void add( Object o ) {
if( o instanceof type ) super.add( o );
}
}

I think is the above is the gist of how EJ handles it. Add a type
token (the "type" variable) and test for types as they are added. If
you want generics too, add them in:

class MySet<T> extends Set<T> {

Class<? extends T> type;

MySet( Class<? extends T> type ) { this.type = type; }

void add( T o ) {
if( o instanceof type ) super.add( o );
}
}

Not compiled, but I think that gives the right idea.

See checkedSet in java.util.Collections.
 
R

Roedy Green

I think this is covered in Effective Java. Generics are a compile time
thing

Everything else in Java eventually seems obvious. Generics on the
other hand get weirder and weirder the more I learn. I think it was a
mistake to try to do generics purely at compile time. It is like
trying to do all types purely at compile time.

Have we gone down that road too far now that Java can never be fixed
with run-time generics info without starting over with some completely
different notation? Then serialisation could work, containers could be
allocated with the precise array type. You would not have so much
under-the-hood casting.

Perhaps it is time to read-read all the generics docs and see if they
make more sense now with some practical experience under my belt.
 
D

Daniel Pitts

Everything else in Java eventually seems obvious. Generics on the
other hand get weirder and weirder the more I learn. I think it was a
mistake to try to do generics purely at compile time. It is like
trying to do all types purely at compile time.
That is exactly what Generics are.
Have we gone down that road too far now that Java can never be fixed
with run-time generics info without starting over with some completely
different notation? Then serialisation could work, containers could be
allocated with the precise array type. You would not have so much
under-the-hood casting.
The point of generics is to add compile-time type safety. Runtime
"generics" is a contradiction to that. It might not have been
implemented in the "cleanest possible" way, but it tends to be good
enough for most non-reflective uses.
Perhaps it is time to read-read all the generics docs and see if they
make more sense now with some practical experience under my belt.
Yes, probably.

In particular, you want to understand what "<A extends B>" and
"<A super B>" mean when declaring generic types, and what "<? extends
B>", "<? super B>" and "<?>" mean when /using/ generic types.
 
M

markspace

Perhaps it is time to read-read all the generics docs and see if they
make more sense now with some practical experience under my belt.


If you feel seriously that you need more explanation, try O'Reilly's
"Learning Java", third edition. It has one of the better explanations
of generics I've found. It's big book to buy just for the generics
section, but not really that much money in the grand scheme of things.
Can you imagine some other way to pay $40 - $50 and get someone to
explain a difficult programming concept? I can't.
 
M

markspace

thanks. I have written an SSCCE to explain this in tedious detail.
See http://mindprod.com/jgloss/generics.html#CONVERTINGSETS


From your example,

static void showDogs2( Set<? extends Dog> dogs )

Is the correct solution. This is exactly what wildcards were designed
for. You should learn the acronym that Joshua Bloch invented as a
mnemonic: PECS - Producer Extends, Consumer Super.

In this case, showDogs2 is a producer in the sense that within the
method body, the variable "dogs" produces dogs for output, but the
method body doesn't put any dogs into the set, or modify the set in anyway.

This

Set<? extends Dog> someDogs2 = someDalmatians;

Is wrong, in the sense that it's too hamstrung to be useful. As your
comments indicate, it's hard to work with, and doesn't really do what
you want either. As a corollary, this

static Set<? super Dog> makeSomeDogs() {...

would be broken for the same reason. You almost never want to return a
type with a wildcard. Just give the user an single type, it will do
what you wanted much better than the wildcard version.

static Set<Dog> makeSomeDogs() {...

Finally, where do you use the super wild card then? Something like this:

static void copy(Set<? extends Dog> from, Set<? super Dog> to) {...
 
L

Lew

Everything else in Java eventually seems obvious. Generics on the
other hand get weirder and weirder the more I learn. I think it was a
mistake to try to do generics purely at compile time. It is like
trying to do all types purely at compile time.

Nonsense. It's perfectly fine at compile time as far as it goes. You want to
catch bugs at compile time rather than run time.

If you want generics at run time then add a type token.

Doing all types at compile time is the whole point of a strongly-typed language like Java. (OK, not all, but as much as you can.)
Have we gone down that road too far now that Java can never be fixed

If it were truly broken that'd be a good question.
with run-time generics info without starting over with some completely
different notation? Then serialisation could work, containers could be
allocated with the precise array type. You would not have so much
under-the-hood casting.

This is an ancient and tired debate. Java does compile-time only with generics.
Sadly, this forces the programmer to fully understand the type assertions they
wish to make, and thus write better code. What a shame.
Perhaps it is time to read-read all the generics docs and see if they
make more sense now with some practical experience under my belt.

Generics is tricky. No question. Its difficulty exactly matches that of
complete type analysis. Type analysis is tricky. No question. Its difficulty
matches that of programming. Programming is tricky. No question.
 
L

Lew

http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

If you feel seriously that you need more explanation, try O'Reilly's
"Learning Java", third edition. It has one of the better explanations
of generics I've found. It's big book to buy just for the generics
section, but not really that much money in the grand scheme of things.
Can you imagine some other way to pay $40 - $50 and get someone to
explain a difficult programming concept? I can't.
 
L

Lew

markspace said:
I think is the above is the gist of how EJ handles it. Add a type token
(the "type" variable) and test for types as they are added. If you want
generics too, add them in:

If you're using Java 5 or later, you not only want generics, you must use them
where the type is generic. Per EJ.
 
A

Arne Vajhøj

The point of generics is to add compile-time type safety. Runtime
"generics" is a contradiction to that.

That was the way it was decided to do it for Java.

But it did have to be that way.

And runtime check can certainly add to type safety!

Arne
 
D

Daniel Pitts

That was the way it was decided to do it for Java.

But it did have to be that way.

And runtime check can certainly add to type safety!
Yes, I never said anything against that. I said generics were designed
to add compile-time type safety. Runtime type safety is a different issue.
 
A

Arne Vajhøj

Yes, I never said anything against that. I said generics were designed
to add compile-time type safety. Runtime type safety is a different issue.

Java generics was designed that way. There are nothing in the
generics concept that says it has to be that way.

Arne
 
D

Daniel Pitts

Java generics was designed that way. There are nothing in the
generics concept that says it has to be that way.
Person A: Look at this green grass.
Person B: But Roses are red!

We're talking about Java generics, not about the concept of "generic"
generics.
 

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
474,430
Messages
2,571,676
Members
48,796
Latest member
Greg L.

Latest Threads

Top