Method chaining with generics

I

iksrazal

Hi all,

Allow my code to explain:

Bsc bsc = BscHelper.createBsc();
SiteType siteType = BscHelper.createSiteType(id,name);
bsc.getSite().add(siteType);

getSite() returns List. It was written without generics in mind.
However, I get a warning in eclipse:

Type safety: The method add(Object) belongs to the raw type List.
References to generic
type List<E> should be parameterized

What it wants is:

List<SiteType> list = new ArrayList<SiteType>();

How can I use method chaining with generics?

iksrazal
 
T

Thomas Hawtin

Bsc bsc = BscHelper.createBsc();
SiteType siteType = BscHelper.createSiteType(id,name);
bsc.getSite().add(siteType);

getSite() returns List. It was written without generics in mind.
However, I get a warning in eclipse:

Type safety: The method add(Object) belongs to the raw type List.
References to generic
type List<E> should be parameterized

What it wants is:

List<SiteType> list = new ArrayList<SiteType>();

How can I use method chaining with generics?

I don't think it's anything to do with chaining. You just need to return
a generified List.

interface Bsc {
List<SiteType> getSite();
}
 
H

Hemal Pandya

Hi all,

Allow my code to explain:

Bsc bsc = BscHelper.createBsc();
SiteType siteType = BscHelper.createSiteType(id,name);
bsc.getSite().add(siteType);

getSite() returns List. It was written without generics in mind.
However, I get a warning in eclipse:

Type safety: The method add(Object) belongs to the raw type List.
References to generic
type List<E> should be parameterized

What it wants is:

List<SiteType> list = new ArrayList<SiteType>();

Almost. javac accepts if getSite returns any compatible generic type:
List<SiteType> or even List<Object>. So Bsc is open this is simple.

Otherwise, you can replace with
((List<Object>)(bsc.getSites())).add(siteType);
for which javac produces a different warnign that is IMHO less severe.
 
I

iksrazal

Thanks for the response.

The class is autogenerated via JAXB, so I can't really just do:

interface Bsc {
List<SiteType> getSite();

However, as stated I do get a different warning now:

((List<Object>)(bsc.getSite())).add(siteType);

Type safety: The cast from List to List<Object> is actually checking
against the erased type List

Hmm. I work in an environment where warnings are discouraged. Any idea
on how to get this to work, ie no warnings, without changing the Bsc
interface?

Thanks,
iksrazal
 
O

Oliver Wong

Hemal Pandya said:
Actually I don't quite understand why cast from List to List<Object> is
a warning at all.

The warning says that the runtime environment can't actually tell
whether the cast succeeds or not (due to type erasure), so basically, the
code that you wrote down is not actually hapenning. I think that warrants a
warning.

- Oliver
 
T

Thomas Hawtin

The class is autogenerated via JAXB, so I can't really just do:

Is there not a more recent versions of JAXB that copes with generics?
Hmm. I work in an environment where warnings are discouraged. Any idea
on how to get this to work, ie no warnings, without changing the Bsc
interface?

If you mix generics with old-style code you are going to get warnings.

Tom Hawtin
 
R

Raymond DeCampo

Oliver said:
The warning says that the runtime environment can't actually tell
whether the cast succeeds or not (due to type erasure), so basically, the
code that you wrote down is not actually hapenning. I think that warrants a
warning.

I think your point is valid when dealing with List and List<Foo>, but
Hemal was specifically referring to List<Object>. Is there any case
when casting a List to List<Object> would cause a problem?

Ray
 
T

Thomas Weidenfeller

Allow my code to explain:

Bsc bsc = BscHelper.createBsc();
SiteType siteType = BscHelper.createSiteType(id,name);
bsc.getSite().add(siteType);

getSite() returns List. It was written without generics in mind.

The real error is an architectural design bug. You have a class Bsc
which contains a list of sites, and you hand out that list to the public
for manipulation. This seriously breaks encapsulation. Class Bsc should
instead get a method addSite(), and do all the list handling internally.

/Thomas
 
H

Hemal Pandya

I think your point is valid when dealing with List and List<Foo>, but
Hemal was specifically referring to List<Object>. Is there any case
when casting a List to List<Object> would cause a problem?

Yes, precisely. I considered if auto-boxing could cause a problem but
am unable to come up with an example.

This makes incremental migration of code from 1.4 to 5 rife with
warnings. Until I have migrated all my code and the third party code
has migrated to using generics.

The reason I like this warning better is that I can filter it out in
javac using "grep -v".
 
C

Chris Uppal

Raymond said:
Is there any case
when casting a List to List<Object> would cause a problem?

I haven't been following the thread, so this may be irrelevant. But if you
start with a List that is intended to hold Strings (whether it's declared as a
List or a List<String> doesn't matter provided it reaches your code as a List)
and cast that to a List<Object>, then you'll be able to add a Rectangle to it,
which would not be correct.

-- chris
 
I

iksrazal

Bsc was autogenerated via JAXB. don't think I can avoid it.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="1.0">
<xs:element name="Bsc">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" name="site">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="id"
type="xs:long" />
<xs:element minOccurs="1" maxOccurs="1" name="name"
type="xs:string" />
<xs:element minOccurs="1" maxOccurs="unbounded"
name="Celula">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="id"
type="xs:long" />
<xs:element minOccurs="1" maxOccurs="1"
name="cell_id" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="unbounded"
name="Trx">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1"
name="id" type="xs:long" />
<xs:element minOccurs="1" maxOccurs="1"
name="name" type="xs:string" />
<xs:element minOccurs="1"
maxOccurs="unbounded" name="Extra" >
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1"
name="id" type="xs:long" />
<xs:element minOccurs="1" maxOccurs="1"
name="name" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

iksrazal
 
H

Hemal Pandya

Chris said:
I haven't been following the thread, so this may be irrelevant. But if you
start with a List that is intended to hold Strings (whether it's declared as a
List or a List<String> doesn't matter provided it reaches your code as a List)
and cast that to a List<Object>, then you'll be able to add a Rectangle to it,
which would not be correct.

Very true. There are two different cases depending on how the List
started. If it was intended to hold Strings but created as a List, not
a List<String> then the error is at the point of creation. This issue
exists in all java until 5.0. If it was created as a List<String> then
the error is where it was converted to List.

But given a List, which by definition is a list of Objects, is there a
case where casting it to List<Object> is dangerous?
 
T

Thomas Hawtin

Thomas said:
The real error is an architectural design bug. You have a class Bsc
which contains a list of sites, and you hand out that list to the public
for manipulation. This seriously breaks encapsulation. Class Bsc should
instead get a method addSite(), and do all the list handling internally.

Not necessarily. If assuming that the implementation of the list
returned keeps bsc in sync, it's fine. List doesn't have to imply
ArrayList/LinkedList.

Law of Demeter, perhaps.

Tom Hawtin
 
O

Oliver Wong

Hemal Pandya said:
Very true. There are two different cases depending on how the List
started. If it was intended to hold Strings but created as a List, not
a List<String> then the error is at the point of creation. This issue
exists in all java until 5.0. If it was created as a List<String> then
the error is where it was converted to List.

But given a List, which by definition is a list of Objects, is there a
case where casting it to List<Object> is dangerous?

This is exactly the issue that the compiler is warning about: Due to
type erasure, there's no way for the runtime to ensure that something which
is a "List" is actually a "List<Object>" and not, for example, a
"List<String>".

public static test(List someList) {
List<Object> listOfObjects = (List<Object>)someList;
}

public static void main(String args[]) {
test(new List<String>());
}

In the above sample code, the runtime will NOT throw a class cast
exception, because it will not be able to detect that the list passed was
actually a list of Strings and not a list of Objects.

Hence the warning in the compiler.

- Oliver
 
R

Raymond DeCampo

Oliver said:
Very true. There are two different cases depending on how the List
started. If it was intended to hold Strings but created as a List, not
a List<String> then the error is at the point of creation. This issue
exists in all java until 5.0. If it was created as a List<String> then
the error is where it was converted to List.

But given a List, which by definition is a list of Objects, is there a
case where casting it to List<Object> is dangerous?


This is exactly the issue that the compiler is warning about: Due to
type erasure, there's no way for the runtime to ensure that something which
is a "List" is actually a "List<Object>" and not, for example, a
"List<String>".

public static test(List someList) {
List<Object> listOfObjects = (List<Object>)someList;
}

public static void main(String args[]) {
test(new List<String>());
}

In the above sample code, the runtime will NOT throw a class cast
exception, because it will not be able to detect that the list passed was
actually a list of Strings and not a list of Objects.

Hence the warning in the compiler.

The point we are trying to make is that it makes more sense (IMHO) to
emit a warning when casting a List<String> or such to a List. Then the
warning on a cast from List to List<Object> could be eliminated and your
scenario is still covered by a warning.

Ray
 
O

Oliver Wong

Raymond DeCampo said:
The point we are trying to make is that it makes more sense (IMHO) to emit
a warning when casting a List<String> or such to a List. Then the warning
on a cast from List to List<Object> could be eliminated and your scenario
is still covered by a warning.

I disagree that that would make more sense: Casting a "List<String>" to
a "List" in itself cannot cause any error to occur (a conceptual error may
occur later on if you try to add an arbitrary object to that list, but if
the compiler could detect this, then the warning should appear where the add
occurs, and not where the casting occurs). However, when you cast a "List"
to a "List<Object>", this IS an error prone operation in the sense that the
runtime can't actually check that your assumption is correct.

I think the problem here is that you think that "List" means "List of
objects", whereas I think that "List" means "List, but I don't know of
what". Under my understanding, if you say "I have a List of String, and I
want to convert it to a List of something unknown", then that should be
fine. But if you say "I have a List of something Unknown, and I want to
convert it to a List of Objects", then that might not be legal, as it might
actually be a list of Strings.

Sort of like casting an object of type String to be of type Object is
always safe (information is lost), but casting an object of type Object to
String is not safe (the programmer is making an assumption here which may or
may not be true).

- Oliver
 
R

Raymond DeCampo

Oliver said:
I disagree that that would make more sense: Casting a "List<String>" to
a "List" in itself cannot cause any error to occur (a conceptual error may
occur later on if you try to add an arbitrary object to that list, but if
the compiler could detect this, then the warning should appear where the add
occurs, and not where the casting occurs).

So should the compiler issue a warning whenever List.add() is called?
After all, the List may have come from a cast from List<String> (for
example, if a public method accepts a List parameter).

The whole danger starts when you throw away information by casting
List<String> to List. There is no ambiguity as to when that happens, so
it is a very good place to issue a warning.

You say that you do not a warning when List<String> is cast to List
because "it cannot cause any error to occur". But in fact it is the
cause; just because it is not in the stack trace at runtime doesn't make
it any less so.
However, when you cast a "List"
to a "List<Object>", this IS an error prone operation in the sense that the
runtime can't actually check that your assumption is correct.

What assumption? I'm "assuming" that I can put Objects in a List. What
is the difference at compile time between a List and a List<Object>
(aside from warnings, which is the crux of the issue here)? How is it
any different from not being able to check that List.add() vs.
List said:
I think the problem here is that you think that "List" means "List of
objects", whereas I think that "List" means "List, but I don't know of
what". Under my understanding, if you say "I have a List of String, and I
want to convert it to a List of something unknown", then that should be
fine. But if you say "I have a List of something Unknown, and I want to
convert it to a List of Objects", then that might not be legal, as it might
actually be a list of Strings.

Aren't there specific notations for things like "List of something
unknown"? I think it is something like List<?>. My understanding is
that List in 1.5 is equivalent to List in 1.4. As far as I can see, I
do not see a difference between List in 1.4 and List said:
Sort of like casting an object of type String to be of type Object is
always safe (information is lost), but casting an object of type Object to
String is not safe (the programmer is making an assumption here which may or
may not be true).

And this generates no warnings. I do not see how it supports either
position.

Ray
 
O

Oliver Wong

Raymond DeCampo said:
So should the compiler issue a warning whenever List.add() is called?
After all, the List may have come from a cast from List<String> (for
example, if a public method accepts a List parameter).

Yes, and in fact, I believe it does:

"Type safety: The method add(int, Object) belongs to the raw type
The whole danger starts when you throw away information by casting
List<String> to List. There is no ambiguity as to when that happens, so
it is a very good place to issue a warning.

You say that you do not a warning when List<String> is cast to List
because "it cannot cause any error to occur". But in fact it is the
cause; just because it is not in the stack trace at runtime doesn't make
it any less so.

I guess we disagree on what the cause of the error is, and I think this
is mainly a conceptual (rather than concrete) issue, so I doubt there's any
way to resolve this disagreement.
What assumption? I'm "assuming" that I can put Objects in a List. What
is the difference at compile time between a List and a List<Object> (aside
from warnings, which is the crux of the issue here)? How is it any
different from not being able to check that List.add() vs.
List<Object>.add() is being used correctly?

The difference between a List and a List<Object> is that you don't know
what you can put in a List, while you know that you can put objects in
List<Object>. I see now that the big problem here is that Java 1.5, while
syntactically backwards compatible with 1.4, is not nescessarily
"conceptually" backwards compatible as illustrated by this misunderstanding.
This is addressed in the next paragraph.

Aren't there specific notations for things like "List of something
unknown"? I think it is something like List<?>. My understanding is that
List in 1.5 is equivalent to List in 1.4. As far as I can see, I do not
see a difference between List in 1.4 and List<Object> in 1.5.

Yes, there does exists what are called "wildcard types", such as List<?>
or even List<? extends Collection>, but there are restrictions on their use,
and they serve a different purpose. You cannot, for example, actually
instantiate an ArrayList<?> (e.g. "new ArrayList<?>") whereas you CAN
instantiate an ArrayList (e.g. "new ArrayList()").

I hadn't thought about it earlier, but now that you've brought it to my
attention, I would say that a List in 1.4 is not equivalent ot a List in
1.5. Rather, a List in 1.4 is equivalent ot a List<Object> in 1.5. And a
List in 1.5 has no equivalent in 1.4, (nor for that matter does a List<Foo>
in 1.5 have an equivalent in 1.4).

And this generates no warnings. I do not see how it supports either
position.

The above example was to show why the operation is unsafe in the first
place. The reason why no warnign is generated in this case is that when the
programmer performs a downward cast (e.g. from Object to String), if the
assumption the programmer made turns out to be incorrect, a runtime
exception will be thrown pointing precisely at this line.

However, in the case of generics, the type of the generic is erased at
runtime, and so no checks can be performed. That means if the assumption the
programmer made turns out to be incorrect, no exception will be thrown at
all, and the program will simply behave in an undefined manner. This
certainly warrants a warning IMO.

Look at this program, for example. Obviously "bad things", conceptually,
are happening there, but if you run the program, no exceptions get thrown or
anything. Obviously, the compiler places warnings here, and for good reason
I think.

import java.util.ArrayList;
import java.util.Date;

public class Test {
public static void main(String args[]) {
ArrayList<String> listOfStrings = new ArrayList<String>();
listOfStrings.add("This is a String");
ArrayList plainList = listOfStrings;
plainList.add(new Object());
ArrayList<Date> listOfDates = plainList;
listOfDates.add(new Date());
System.out.println("If you see this, it means the program ran
successfully with no cast errors.");
}
}


- Oliver
 
J

John C. Bollinger

Oliver said:
[...]
What assumption? I'm "assuming" that I can put Objects in a List. What
is the difference at compile time between a List and a List<Object> (aside
from warnings, which is the crux of the issue here)? How is it any
different from not being able to check that List.add() vs.
List<Object>.add() is being used correctly?


The difference between a List and a List<Object> is that you don't know
what you can put in a List, while you know that you can put objects in
List<Object>.

A little more precision might help. You know that in principle you can
_safely_ put *some kind* of object in a raw List, but you don't a priori
know *which* kind. You therefore do indeed make an assumption when you
put any kind of object in such a list. On the other hand, you know that
you can put Objects in a List said:
I see now that the big problem here is that Java 1.5, while
syntactically backwards compatible with 1.4, is not nescessarily
"conceptually" backwards compatible as illustrated by this misunderstanding.

I disagree. I think the issue is more that the type safety problem was
so pervasive before 1.5 that many people formed incorrect
conceptualizations. If I create a List that I intend to hold only
Strings, then it should be regarded as a List<String> whether or not I
have generics available with which to express the concept. It is
certainly NOT a List<Object>, even though the compiler will not prevent
me from accidentally treating it as one. Careful programmers writing
without generics will document this sort of thing very clearly, which
has many of the effects of generic declarations with regard to other
humans reading the code. From a code analysis point of view, however,
you cannot tell from the declaration what type an instance may contain,
nor what types it is safe to add. This is precisely the situation that
generics are intended to clear up.

[Oliver wrote:]
Quite right. And if I take your List of Strings and treat it as a List
of Objects, then I might add an Integer to it. Or a plain Object. When
you come back to check the length() of each member your program will bomb.

Yes, a raw List is the same thing in 1.4 and 1.5. It is the only kind
available before 1.5. Because it wouldn't have made any sense for the
compiler to issue type safety warnings before 1.5, a raw List in 1.4 may
seem as if it should correspond to a List<Object>, but that's not the
case. When you write List<Object> you are asserting that it is safe to
add any object the list, but this is in fact rarely the case, whether in
1.4 or in 1.5, and indeed it was not the case in the OP's example. The
raw List type corresponds most closely to List<?>, the main difference
being that in the latter case you are explicitly stating that you don't
know what type you can safely add to or expect to remove from the List,
whereas in the former case that's implicit.
I hadn't thought about it earlier, but now that you've brought it to my
attention, I would say that a List in 1.4 is not equivalent ot a List in
1.5. Rather, a List in 1.4 is equivalent ot a List<Object> in 1.5. And a
List in 1.5 has no equivalent in 1.4, (nor for that matter does a List<Foo>
in 1.5 have an equivalent in 1.4).

Nope. You were going strong up to there, Oliver, but you just jumped
the track. Raw Lists are equivalent between 1.4 and 1.5 from a class
file standpoint, for one thing, but overall it amounts to what you can
prove about types. In that sense, as I described above, the raw List
type is most like List<?>.

[...]
when the
programmer performs a downward cast (e.g. from Object to String), if the
assumption the programmer made turns out to be incorrect, a runtime
exception will be thrown pointing precisely at this line.

However, in the case of generics, the type of the generic is erased at
runtime, and so no checks can be performed.

That's perhaps a bit misleading. Type parameters are indeed subject to
erasure, but an object always has a known, well-defined class, and
downcasts can always be checked. It's just that the type system in Java
1.5 has a dimension (type parameters) that the runtime ignores.
That means if the assumption the
programmer made turns out to be incorrect, no exception will be thrown at
all, and the program will simply behave in an undefined manner. This
certainly warrants a warning IMO.

You make it sound as if there were a security problem with 1.5 JVMs
here, and that simply is not the case. Yes, a cast from List<Object> to
List<String> cannot by typechecked at runtime (in fact, as you say,
there isn't actually a runtime cast at all), but when I take an object
out and try to assign it to a String variable or invoke a String method
on it then you can be sure that a ClassCastException will be thrown.

I agree that the warning is merited, given that one of the stated goals
for generics was to enable programmers to assure type safety, but
nothing catastrophic will happen if the warning is ignored and some
particular cast happens to be incorrect.
 
R

Raymond DeCampo

[snipped a whole bunch of things about List, List<?>, List<Object>, etc.]

Let's boil this argument down a bit. The crux of the matter seems to be
how we are viewing a List. My viewpoint is that a List is equivalent to
List<Object>. They are both "lists of Objects". A List<?> is a "list
of a specific unknown thing."

The viewpoint of Oliver and John appears to be that a List is not
necessarily the same as List<Object>; a List is a "list of a specific
unknown thing" and List<Object> is a "list of objects". List<?> is a
"list of a specific unknown thing."

Hopefully I have the above right. As an aside, let me say that I am
approaching this in a purely academic manner, as I have not actually
done any real-world generics programming at this point. So I am very
interested in the perspective of others who have done so.

First, let us agree that these are conceptual, not practical
differences. However, conceptual differences are important, especially
in a case like this where information from the source code is discarded
by the compiler.

Here is the reasoning behind my viewpoint:
1) List is conceptually the same between 1.4 and 1.5. This
obviously eases the transition from 1.4 to 1.5.
2) When dealing with a generic list like List<String>, I would
endeavor never to cast it to a List, but always to a List<?>, List<?
extends Foo> or List<? super Foo>. (Note that we agree on the
conceptual definition of List<?>.) Casting it to a List throws away the
fact that it is a generic list.

Finally, let me say that this whole discussion really shows how Sun
screwed up generics from an OO perspective. It is reminiscent of the
modifiable/unmodifiable collections disaster.

Ray
 

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

Similar Threads

generics puzzle 57
subclassing and generics 19
generics and type checking 9
1.5: generics warning of using Raw datatype 7
Tree design with generics 2
Class Generics 4
JDK5 Generics 6
Generics compiler warning 3

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top