My generics test in Java doesn't work!

I am doing some Java studying, especially in the area of generics.

I'm fairly familiar with generics in C#, but in Java, it's a whole different story.

I used a few samples that worked just fine for testing, and I am able to replicate most of my C# code in Java just fine.

However, when I try the following example, it doesn't work:

private static <T> void swapKundeData(ArrayList<T> data, int index1, int index2) {

    T temporary = (T) data.get(index1);

    data.set(index1, data.get(index2)); //Does not compile
    data.set(index2, temporary); //Does not compile

}

The error I am receiving is:

The method set(int, capture#5-of ? extends ExtendTest) in the type ArrayList is not applicable for the arguments (int, ExtendTest)

An equivalent of this works just fine in C# - so what's going on?

I've read that Java has received a lot of criticism when it comes to generics. Is this a part of that criticism? The Remove and Add method of the data variable works just fine.

Upvotes: 2

Views: 250

Answers (6)

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147164

I note that the question has been edited such that the previous answers don't make much sense.

From looking at the question edits, it worth noting that if you want clients to see a method like:

public static void swapKundeData(List<?> data, int index1, int index2) {

But you want to implement:

public static <T> void swapKundeData(List<T> data, int index1, int index2) {

The wild card can be captured and a method with the client-friendly form can forward to an implementation-friendly form:

public static void swapKundeData(List<?> data, int index1, int index2) {
    swapKundeDataImpl(data, index1, index2)
}

private static <T> void swapKundeDataImpl(List<T> data, int index1, int index2) {

(I'm assuming here that "kunde" is not a rude word.)

Upvotes: 1

Geniedesalpages
Geniedesalpages

Reputation: 418

Well for one, I would use

List<Kunde> 

instead of

ArrayList<?>

since you are casting to Kunde anyway :).

Reason why it doesn't work is that you don't know the type of the objects passed. So if you set a Kunde that could possibly be the wrong type (since with ArrayList < ? > you can pass ArrayList < String > and setting a Kunde on that would be the wrong type).

Another possibility is:

private static <T> void swapData(List<T> data, int index1, int index2) {

    T temporary = data.get(index1);

    data.set(index1, data.get(index2)); //Does compile
    data.set(index2, temporary); //Does compile

}

And to complete my rant, just use the swap method of Collections.

http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#swap(java.util.List, int, int)

Upvotes: 6

The problem was Eclipse.

I had not saved the file, so apparently nothing was recompiled.

Wow, I am really impressed of that IDE's incapabilities and annoyances, such as speed.

Oh well.

Upvotes: 0

Michael Borgwardt
Michael Borgwardt

Reputation: 346317

The reason for your problem is that you're declaring a type of ArrayList<?>, which means "A list that should only contain a certain type, but I don't know which one". That has the consequence that the only thing you know about objects taken out of the list is that they're of type Object because everything is (except primitives, which cannot be in a collection). But even more importantly, you cannot put anything into the list because you don't know to what type it is constrained.

This is why using the ? wildcard is very rarely appropriate. In almost all cases you should either use a concrete type, or a named bounded type:

private static void swapKundeData(List<Kunde> data, int index1, int index2) {
    Kunde temporary = data.get(index1);
    data.set(index1, data.get(index2));
    data.set(index2, temporary);
}

Note the use of the List interface, which makes the code much more flexible about what it accepts.

private static <T> void swap(List<T> data, int index1, int index2) {
    T temporary = data.get(index1);
    data.set(index1, data.get(index2));
    data.set(index2, temporary);
}

This is the really generic solution, which is typesafe, yet does not need to know what type the list elements are. You actually don't really gain any type safety by using Generics here (Object would do just as well), but you could e.g. return one of the swapped elements and the compiler would know that the return value is the same type as the list's elements.

Upvotes: 1

xaxxon
xaxxon

Reputation: 19761

To summarize what Oli is saying, you can't assign to a collection of the "unknown type" '?' because java doesn't know what's supposed to go in there. However, since every "unknown type" is still an object, it's safe to call "getter" methods on it because they'll always return an object of type Object.

Upvotes: 0

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272517

If you know that your ArrayList contains Kunde objects, then you should declare the method argument ArrayList<Kunde>; there's no need to use a wildcard. And as you can see, it causes compile-time type errors.

See http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html for the full explanation of the problem.

Upvotes: 2

Related Questions