• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

Purpose of Wild Card in Reference

 
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I argued in a previous topic that you should not use wild cards for concrete references. A few topics later somebody else did precisely the same thing and in this case it worked. I'll use simpler variable names to make it more clear.

IMO this is pointless when it works and generally it doesn't.



This achieves the same result with the added benefit of always working.



Now, this was a later topic where super was used. This has more utility in that there is no equivalent non-generic reference declaration. This really does buy you some added type safety. Sadly, it generally doesn't work.



Now, why don't these work in general? Because if you add a method to the Container that accepts a wild card type (e.g., add(Value<?> ; ) ) then it will generate a compile error when you try to call it through that reference:



So, I'm claiming that you should only use wildcards to create generic methods and class definitions, not as concrete references. I did some wall-banging on that ealier. I might be off base with that policy, not experienced enough with generics yet to say for sure if it's sound.

[ August 12, 2005: Message edited by: Rick O'Shay ]
[ August 12, 2005: Message edited by: Rick O'Shay ]
 
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Those three declarations mean different things. Each of them has a use and should be used if and only if that's the declaration you intend.

Container<? extends IFace> means you can take an IFace out of the container but you can't put anything in.

Container<IFace> means you can take an IFace out or put one in.

Container<? super IFace> means you can put an IFace in but you can't take one out.

Using Container<IFace> when you mean Container<? extends IFace> is like declaring an ArrayList when all you really need is a List or even just a Collection. Sure, you can write all you code such that it only uses the ArrayList type and never the List type. In that sense ArrayList "always works." But if decide you want to use a LinkedList someone or you call a library function that returns a List instead of an ArrayList you're going to have to rewrite all those declarations!

It's important that programmers understand the distinction between Container<? extends Value> and Container<Value> so they can use the precise declaration they mean. Even if you're sure there's no programmatic reason why the declaration Container<? extends Value> is *necessary*, the type declaration conveys useful information to someone reading your code.
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:



This achieves the same result with the added benefit of always working.




I don't think so. Try



with both versions.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
>> foo = new Container<MySpecialValue>();

That's not the goal and using a wild card in foo brings you full circle back to the original topic! I am suggesting that you not use the wild card in this case.

The goal is to create a Container that will accept anything of type Value. This was offered as a solution but it broke when an add was attempted:

Container<? extends Value> container = new Container<Value>();

My suggestion, getting back to the original question, is to use this because it achieves the same result with the added benefit that it always works, no pesky problems with add and so forth:

Container<Value> container = new Container<Value>();

I then expanded that to say you can avoid these problems using wild cards only when defining generic methods and classes.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
>> Container<? extends IFace> means you can take an IFace out of the container but you can't put anything in.
>> Container<IFace> means you can take an IFace out or put one in.
>> Container<? super IFace> means you can put an IFace in but you can't take one out.

That would be great... if only it were true. The compiler doesn't know push from put from set from shinola. The issue is that you have methods whose parameters are the generic type and NONE of those methods, regardless of what they do, can be invoked through the wild card reference.

Trying to bend an implementation artifact in to a useful feature is a good idea but stipulating that wild card references are read-only is false. I'm still seeing no benefit, maintenance or otherwise, in using them outside of a generic class or method definition.

My opinion: don't use them because you will have better code clarity using non-wild references. Still generic, but not wild card. The purpose of wild card references is to unbound or crate range-bound method parameters. That you can use them otherwise is an artifact best avoided.
[ August 13, 2005: Message edited by: Rick O'Shay ]
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Rick, I'm sorry, but you are simply plain wrong.

The following code compiles and runs without any problems (tried with Eclipse 3.1):



It works because the wildcard guarantees that every object in the collection will be a Number and therefore understands the doubleValue message.

It doesn't compile anymore if you remove the wildcard, because none of the singleton lists are Number lists - they are Integer or Double lists.

A similar example can be constructed for super.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:
The compiler doesn't know push from put from set from shinola.



It doesn't need to - it just needs to distinguish between method arguments and return values, which it clearly can.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:
My suggestion, getting back to the original question, is to use this because it achieves the same result with the added benefit that it always works, no pesky problems with add and so forth:

Container<Value> container = new Container<Value>();



Yes, that was the right solution for the original problem, full agreement.


I then expanded that to say you can avoid these problems using wild cards only when defining generic methods and classes.



Your argument that not using wildcards outside of defining generic methods and classes always works simply is wrong. The solution is not to blindly only use wildcards when defining generic methods and classes, but to understand how wildcards work and then use them whenever appropriate.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The example you gave fails:



Oh, wait. I must have accidentally changed "get" to "add". In other words, we're going in circles. I understand entirely that you example can and will and does work, but notice how in general it does not.

I think really that's the only disagreement. I'm saying it's not orthogonal and it's not even consistent so you lose more than you gain. It's simply an artifact of Java generics. That's not just plain wrong it's how I am interpreting that feature.

Now, we agree on the Shape from Shinola reference. I was pointing out that David's interpretation was wrong: you cannot add through wild card bounded references was the claim. That's not the case, it's ANY method that accepts a generic parameter. That's why it is inconsistent and why I say avoid it.

We also agree that everybody should understand the distinction between the specific and unspecified reference types. We even agree that they should be used appropriately having grasped the distinction. I think we just disagree on where it is appropriate to use it.

Anyhoo, this has been an enlightening discussion and forced me to dig deeper in to generics so it's all good.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:
Oh, wait. I must have accidentally changed "get" to "add". In other words, we're going in circles. I understand entirely that you example can and will and does work, but notice how in general it does not.



I don't understand what you mean by "in general". I think my example showed that *not* using wild cards doesn't work "in general" either. It simply won't compile in my example.

Use the right tool for the right job. And wild cards can be the right tool for local variables, too, as I showed.

I think really that's the only disagreement. I'm saying it's not orthogonal and it's not even consistent so you lose more than you gain.



No, you don't lose more than you gain. You lose one thing and gain another. Which one is the right thing to do depends on the circumstances.


I was pointing out that David's interpretation was wrong: you cannot add through wild card bounded references was the claim. That's not the case, it's ANY method that accepts a generic parameter.



Well, yes. And for collections that means that you cannot write, but can read (if we talk about "extends" wildcards - for "super" is exactly the other way around). I don't see any inconsistency here.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What I mean by does not work in general is as follows:

A. Container<Foo> foo; // Can be used for any Foo method
B. Container<? extends Foo> foo; // Cannot be used for any Foo method

My example always works for the type in question: Foo for example. It's really going off in the weeds to change that to someting other than Foo. Question is to use A or B and I say A with concrete type assignment, no matter what the circumstance.

Just so we're in sync, can you explain when the second form fails on some methods? Writing versus reading is not the reason. That's plain wrong. The idea that with super the reverse is true is also wrong. Sounds like you're buying David's incorrect assertions and just repeating them.

The reason that it fails on some methods (read: does not work in general) is that any method that accepts the parameterized type is not compatible with the wild card. I'm certain that's not sinking in because the bogus argument about "you can't write" keeps coming up.

Angelika Langer explains this very clearly! Now, if you look at the rules, you can get the reason why I find the attempt to declare a field with a wildcard and use it with a concrete is a giant leap backward -- except as I have repeatedly stated, when generating generic methods and classes. Now, you can memorize the rules and analyze each class you use it with (there is no consistency between classes in this regard) or go for code clarity and lose nothing of value in the process.

http://www.langer.camelot.de/GenericsFAQ/FAQSections/TechnicalDetails.html#Wildcard%20Instantiations

I keep repeating when this is the right tool for the job and when it isn't based on the restrictive nature of this construct. Clearly I have some very solid notions of when to use it and when not to but I can't say I'm getting any coherent rebuttals. "Use the right tool for the job" isn't a rebuttal, it's basically restating my argument.
[ August 13, 2005: Message edited by: Rick O'Shay ]
 
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Haven't gone through each and every word in this post, but this debate about using or not using wildcard instantiations - seems to have some "opinionated" rationalizing.
For e.g., someone mentioned

Container<? super IFace> means you can put an IFace in but you can't take one out.



This isn't correct - super essentially lets you use all generic instance methods that as long as you are passing the bound instance or its subtype.

More on this is available here : http://www.sachinahuja.com/TigerNotesIndex.html

- Sachin
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There was an attempt to simplify the use of bounded wild cards as expressing read-only versus read-write. That indicated to me an incorrect interpretation of a collection example rather than an accurate understanding. That translated to not really getting the gist of my opinion leading tail chasing arguments.

I based my opinion on accurate knowledge of generic type compatibility as described by Angelika Langer. It's not a question, rather a usage pattern which is subjective.

I consider this to be poor usage of wild cards:

Container<? extends Product> container = new ContainerImpl<Product>;

Great, you have a product container and some how you gain a benefit from restricting access because of type incompatibility? Instead do this:

Container<Product> container = new ContainerImpl<Product>;

It was not an argument against wild cards but that if you want to reference a Container of Product or Product sub-types and you are not writing generic methods or classes, just you the dang concrete type.

That's all it was but initially there was no understanding that the wild card had restrictions (pounded wall on that one) and after clearing that up there was no understanding of what drives the restrictions, leading to the bogus stipulations about their being read-only.
[ August 13, 2005: Message edited by: Rick O'Shay ]
 
Sachin Ahuja
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Rick,

Good explanation, here's the deal though - the language gives us certain spec ... and Langer and other's including you and I make design decisions based on the limitation and powers of the language. In my notes, I have tried to rationalize the specication because its a little cryptic. As for the design "best practices" - Tiger's too new to have any best practices at all .. lets implement them in a couple of projects and then perhaps we can evaluate the results to reach best practice examples.

Till then, let these forums be the debating ground for evolving these "best practices"

- Sachin
 
David Weitzman
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The compiler doesn't know push from put from set from shinola

I'm assuming you mean Container<T> to be a standard container like a reference or a java Collection. Certainly you could define Container<T> as follows and what I said would not apply:



The read-only vs. write-only interpretation applies only to containers that act like containers. In general it's a question of abstraction. Again, I don't deny that you could make every type parameter in your program concrete and every list in your program an ArrayList, but years of coding history have shown that "Use the most general interface that works" is a sound principle. <? extends/super Type> is more general than <Type>.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The choice of "Container" was to imply an arbitrary class not a specific implementation. It made sense given it was not a question about the Java collection interfaces but rather bounded wild cards.

The main point, which happily has been made, is that it's the presense of the generic type in the method parameters that's relevant. Coincidentally, methods that add or put in collections fall in to that category. Generally speaking, mutables may compile just fine while immutables might not (the very opposite of how the standard collections happen to be implemented). It all comes down to the method parameters not read versus write.
[ August 13, 2005: Message edited by: Rick O'Shay ]
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ah, now I see the root of one of the confusions, I guess. I was, as David, implying that a Container's writing methods would get a generic type passed, and that reading methods would return a generic type. This seems kind of natural to me, and of course *from that* David's rules follow directly. It probably would have been better to state that more explicitely.

I still have an issue with recommendation of not using wildcards for variable declarations. I don't see how you could do without wildcards in my example. Perhaps you can show us how to do it?
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It is natural that collection write methods would accept the type parameter and that read methods would return the type parameter. It's also natural to pass the generic type to a Map or Collection that strictly reads. The rules don't follow, even with the standard containers.

> Set set = treeSet(bookmark).tailSet();

Now, how would you implement your example withou wild cards? I take it you mean how can this work:

> Container<General> container = new Container<Special>();

Remember, my guideline suggests that in garden variety code you don't use the wild card; rather, you reserve that for generic methods and classes. I have been making that narrow distinction from the start. With that in mind here is what I would do:

> Container<Special> container = new Container<Special>();

David complains that is like using ArrayList for List. I say, yes, you do have a more specific type but quietly losing half your interface is worse. On balance, my preference is to avoid the wild card in that situation.
 
David Weitzman
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
David complains that is like using ArrayList for List. I say, yes, you do have a more specific type but quietly losing half your interface is worse. On balance, my preference is to avoid the wild card in that situation.

What you call "losing" some would call "hiding." The purpose of a wildcard is to hide parts of the interface that shouldn't be used. If you actually need to use those parts of the interface then not hiding them is definately the correct solution.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
>> David complains that is like using ArrayList for List.

I would also add the following. A new requirement comes in. The solution requires a method call through the wild card reference. It won't compile because it accepts a type parameter. UPSADAISY! You need a specific type reference. So much for change insulation.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
>> The purpose of a wildcard is to hide parts of the interface that shouldn't be used

FALSE!

The purpose is to stipulate an unknown or bounded type. That is to operate on a set of generic types rather than a specific one. The restricted access is a side-effect designed to retain type safety.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Rick, I'm beginning to get a little bit upset, as it seems that you haven't even looked at my example. Your "solution" doesn't work for it, because I assign different types of lists to the reference.

Regarding the purpose of the wildcard: it gives you more flexibility in what kinds of objects the reference might point to, while as a consequence limiting what you can do with the object it references. It's really very similar to the List/ArrayList issue.

With other words, using a wildcard gains you flexibility in one dimension, and reduces it in another. Which dimension is more important clearly depends on the specific circumstances and needs to be balanced case by case. A sweeping statement doesn't help at all with this.
[ August 14, 2005: Message edited by: Ilja Preuss ]
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What do you mean "lookup your example"? Wasn't the snippet I posted essential the example you had in mind? Show me where the example and I'll review it with respect to my proposed use policy for the wild card. I thought that was it but apparently not.

BTW, we have 21 messages and still getting entirely wrong assertions about the purpose of wild cards:

>> The purpose of a wildcard is to hide parts of the interface that shouldn't be used

WRONG!!! WRONG!!! WRONG!!!

BTW, when you say upset, do you mean:








[ August 14, 2005: Message edited by: Rick O'Shay ]
 
David Weitzman
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The purpose (of a wildcard) is to stipulate an unknown or bounded type.

And what, do you suppose, is the purpose of a type? Why does the List interface exist? Is the purpose of the List interface to hide information about the concrete List implementation or to stipulate an unknown subclass type? One of the foremost principles of software design is "information hiding." There are many reasons to hide information. One reason is because you simply don't know, or as you would say the type is "unknown." Perhaps object was instantiated by code you didn't write, or by reflection based on user input.

An equally valid reason to hide unnecessary type information is because it makes code hard to change. Perhaps today I know that every list in my program is an ArrayList, but tomorrow someone may want to pass a LinkedList to one of my methods and there's no reason that method shouldn't work on a LinkedList except that it was declared to take an ArrayList. Software that isn't modular is a pain to work with, if you can even work with it at all.

Information hiding encompasses both these reasons, and perhaps some others. Static typing does much more than allow information hiding, of course, but you'd have a tough time finding a statically typed language that doesn't support the idea of private information.
[ August 14, 2005: Message edited by: David Weitzman ]
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:
What do you mean "lookup your example"? Wasn't the snippet I posted essential the example you had in mind? Show me where the example and I'll review it with respect to my proposed use policy for the wild card. I thought that was it but apparently not.



OK, I see that you referred to my first short snippet. I was actually talking about the one a few posts later. I will repeat it here for your convenience:




BTW, we have 21 messages and still getting entirely wrong assertions about the purpose of wild cards:

>> The purpose of a wildcard is to hide parts of the interface that shouldn't be used



Well, wildcards *can* certainly be used for that purpose, can't they? Perhaps there is more than one way to look at it...
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
David, you use a lot of platitudes. Use information hiding, use the right tool, et cetera. I have nothing against repeating the obvious but not when they obscure simple questions.

I won't repeat myself. You can read the specification, look at Angelika Langer's site and so on. There you will see printed what I assumed was an obvious fact: the purpose of wild card. You will also find a discussion of side-effects, to wit, restrictions. What you will not find is a stipulation that wild cards are for hiding methods.

If you access an Integer reference without initializing it it will throw a NullPointerException. Therefore, do we assume the purpose of Integers is flow control? That's asinine.
[ August 14, 2005: Message edited by: Rick O'Shay ]
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
>> Well, wildcards *can* certainly be used for that purpose, can't they? Perhaps there is more than one way to look at it...

Sure, and one way to look at int is for counting sheep but it's not clear that's the purpose of integers. There is no way to spin the purpose of wild cards as a facility for hiding methods when used through certain reference types.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ilja, I did see that snippet. I just ran it through a noise filter and this was the only relevant part remaining:

List<? extends Number> list = Collections.singletonList(new Double(3.14));

Here's the solution, which suffers from no restrictions:

List<Double> list = Collections.singletonList(new Double(3.14));

The singletonList call is a distraction but you need it for your example.
 
Wanderer
Posts: 18671
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Rick, you might want to switch to decaf in the afternoon. Yelling "FALSE!" and "WRONG!!! WRONG!!! WRONG!!!" is quite unnecessary. And your "noise filter" is an unnecessarily obnoxious comment indicating that you've simply disregarded the part of Ilja's code which was made the wildcard necessary. Seems a shoddy tactic - you can do better. And I bet you can also do it while maintining a polite conversation.

Regarding the "purpose" of wildcards: it sounds like you and Ilja are using different language to describe these concepts from different directions. Ilja description of a wildcard as a way to hide parts of an interface that shouldn't be used makes sense to me. It's not the only way to describe a wildcard, and I woulnd't say it's complete, but it does describe a primary effect of using wildcards, as I see it.

Rick, in your original post you refer to "concrete references". I'm not entirely sure what you mean by this - would a non-primitive local variable be an example? (Once we put in a wildcard I'm not sure how "concrete" it is.) I'll assume a local variable is a valid example, which I will use in subsequent discussion. Rick, you originally said (referring to using wildcards while declaring concrete references):

"IMO this is pointless when it works and generally it doesn't."

I would agree that it's often unnecessary, especially in "garden-variety code" as you say later. Garden-variety code often doesn't need wildcards at all, and when they are needed it's typically in the method declaration or in the type paramenter of a generic class. But I assert that there do exist cases where a wildcarded local variable not only works, but is necessary. (Or at least, it's more elegant than other possible workarounds.) You seem to have acknowledged that a wildcard in a local reference can work, but as near as I can tell you still regard it as "pointless". Have I understood you correctly, or did I overlook a later recant of that part of your original post?

So, what's an example where using a wildcard in a local varible declaration is not pointless? Ilja's code above may qualify, though it's very short and asomewhat contrived, I think; I have a bit of difficulty imagining a larger context in which I'd need something like that particular piece of code. However, there are examples where the need for wildcards in local variables arises much more organically. A good place to find some is in the source code to java.util.Collections. Let's take this example:

Obviously this uses wildcards in the method parameters (whic are of course another type of reference, but wildcards in parameter declarations are so ubiquitous I assumed that must not be what you meant by a "concrete reference".) What interests me here is the use of wildcards in the declarations for the two ListIterators, di and si. Is there another, better way to write this method without using those wildcards? I believe the answer is no. If you can find a way, please post it.

It's tempting to try something like this within the else block:

However this won't work for all types of List we might encounter; in particular it won't work for a fixed-size list such as that returned by Arrays.asList(). That's why the ListIterator's set() method is being used instead.

It's also a bit tempting to replace the nameless wildcards with named type parameters, like this:

The problem is, that won't compile. Although "B extends T" is legal in a type parameter, "A super T" is not. This is a subtle difference between a type variable and a type argument. Basically you can use "? super T" in a parameter declaration, or a local variable declaration, but you can't use "A super T" as part of a type parameter for a generic type or generic method. I'm not sure why this is the case, but both the JLS and the compiler seem to agree; you can't do it.

Even if you could do it - would the above code be any better than what's in the source now? The difference is minor either way, however I believe that using A and B above is slightly more confusing. In particular it puts A and B into the API. Someone looking at the javadoc may wonder what A and B refer to, and are they used elsewhere? This might be useful if either A or B appeared as part of a return type. But there's no return, so it seems rather pointless.

Here's a hybrid version that does compile (and work, I think):

This gets rid of one of the wildcarded local variables, but not the other. This is legal, but to my mind it's more confusing than either of the other two, using two different styles side by side. I only include it for comparison.

Again, I'm not arguing that use of wildcards in local variables is common. It's not. I'm just arguing that in some cases it is not only legal - it's the best solution.

Cheers...
[ August 14, 2005: Message edited by: Jim Yingst ]
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The noise filter was simply an indication that some of the code contained no information relevant to the topic and was removed for clarity. That's not an uncommon term and it's not obnoxious or at least not intended to be.

As far as removing code, noting was removed that pertained to the question or the answer that I provided. Not sure where you get that from.

Now, as far as the collections example, I maintain that's where you use the wilc card: creating generic methods and classes. They are necessary when the invoking object wants to use other generic types.

Example: Foo<T> has a method that adds two Ts:

int Foo.add(Foo<T> foo)

That's fine, but unless you make that Foo<?> you will not be able to add fooa to foob or vice versa:

Foo<Integer> fooa;
Foo<Long> foob;

The reason is the types don't match. So you need Foo.add(Foo<?> foo)

What I do not want to see is programmers writing this:

Foo<?> foo = new Foo<Integer>(); // Ridiculous!
[ August 14, 2005: Message edited by: Rick O'Shay ]
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Continuing. The upper and lower bounds (extends and super) are sort of side issues in terms of using wild card. They give you additional functionality, however, it's still just a wild card.

I would use a bound to gate access via the compiler. Say I had a generic metrics collector that used mass, volume and velocity. However, only some objects in my family have velocity. You can't compute arrival time without velocity so I will restrict this generic method to Sprite objects knowing Sprite and its sub-types have velocity:

int getArrivalTime(Metrics<? extends Sprite> thing) ...

This is the only elegant solution to the problem.
 
David Weitzman
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Edit: Ah, damn. I forgot that quotes get eaten if you edit

"Stipulating an unknown type" is not a end, it's a means to an end. From On Variance-Based Subtyping for Parametric Types (Igarashi and Viroli, 2002):

Variant parametric types are used to control
both subtyping between different instantiations of one generic class
and the visibility of their fields and methods. On one hand, one parametric
class can be used as either covariant, contravariant, or bivariant
by attaching a variance annotation�which can be either +, -, or
*, respectively�to a type argument. On the other hand, the type system
prohibits certain method/field accesses through variant parametric
types, when those accesses can otherwise make the program unsafe. By
exploiting variant parametric types, a programmer can write generic code
abstractions working on a wide range of parametric types in a safe way.


So the purpose of generics is simply to support typesafe code reuse. Agreed? Ok.

Bounded and unbounded wildcards exists for the same reason generics exist (after alll, they're a part of generics): typesafe code reuse. The covariant bound exists so you can write code as follows:



Agreed?

So what can't work if the method is declared like this?

void method(GenericClass<GenericParam> arg)

Because GenericClass<T> is a subtype of GenericClass<S> if and only if T and S are subtypes of each other the non-wildcard version of method() asserts too many invariants, too much information about its parameters. It requires that the type parameter be invariant. Thus it won't accept a GenericClass<SubtypeOfGenericParam>. The idea of wildcards is that by using values in a way that doesn't depend on the invariance of their type parameters you can write more generic, reusable code.

Now since you can't directly construct a type with a non-invariant type parameter, just introducing the idea of a non-invariant type is useless. The remaining piece of the puzzle is that invariant types are always subtypes of their corresponding covariant and contravariant types.

So what I mean when I say non-invariant parameters are a form of information hiding is that the application of the subtyping rule

C<..., S, ...> <: C<..., +S, ...>

loses information in the same way assigning an ArrayList to a List loses information.

So yes, I was being shortsighted when I said the purpose is to "hide information." The purpose of wildcards, strictly speaking, is neither to hide information nor to "stipulate an unknown or bounded type." Those are subgoals to the greater goal of supporting typesafe code reuse. Certainly it's important to be able to convert a concrete type to a bounded type as well as to refer to a bounded type, since if either of those steps were missing you would never make it to the actual goal of code reuse.

The following paragraph is entirely opinion:

Since the entire goal of generics is to facilitate code reuse, it seems in keeping with their spirit to use the most general type that works whenever possible. That means using unbounded wildcards in preference to bounded wildcards, bounded wildcards in preference to concrete types, and concrete types as necessary.

[ August 14, 2005: Message edited by: David Weitzman ]
[ August 14, 2005: Message edited by: David Weitzman ]
 
Jim Yingst
Wanderer
Posts: 18671
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
[Rick]: As far as removing code, noting was removed that pertained to the question or the answer that I provided. Not sure where you get that from.

I was referring to the fact that Ilja's code was set up so that the variable "list" could refer to either a List<Integer> or a List<Double>. That was what required the variable to be declared as List<? extends Number>. You removed the List<Integer> part of the code, which allowed you to replace List<? extends Number> with List<Double>. By dropping that code, you changed the nature of the problem too much. Your answer is not relevant to Ilja's original code - it's only relevant to your modified version of the code.

Now, as far as the collections example, I maintain that's where you use the wilc card: creating generic methods and classes.

OK. I was taking a different interpretation of what you had meant by that; I won't explain it in detail because, while it made sense at the time, my interpretation was wrong, and the details would just be a lengthy sidebar that doesn't really matter at this point.

Ilja's example does require a wildcarded local variable, but so far I can't really imagine a good motivation for it. I.e., why would we want to write such a method? Whenever I try to imagine a context in which a wildcarded local variable is useful for some real-world problem, I seem to bring in other generic methods or classes. So I guess I don't have a substantial disagreement with what you're saying on this front. Though I haven't completely rules out the possibility that there may be a good real-world example out there somewhere - but it seems like it's a fairly rare situation to find when coding. Anyway, I think I'll bow out now; carry on (nicely) ...
 
David Weitzman
Ranch Hand
Posts: 1365
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
"Now, as far as the collections example, I maintain that's where you use the wilc card: creating generic methods and classes."

I don't know exactly what you mean by creating generic methods and classes, but take this code for example:



Would you consider the actions and it local variables a good candidate for a bounded wildcard? I don't see any generic methods or classes being created here, but this code makes me uneasy. The time may come when all Actions in the program extend CustomAction, and then changing actions to have type Set<CustomAction> and getActions() to return Collection<? extends CustomAction> will require fixing resetActions() and registerKeybindings() and any other users that don't ignore type information they don't need.
 
Jim Yingst
Wanderer
Posts: 18671
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
David, if we need to I don't think the getActions() method is that much of a problem; you can code up an adaptor which gives a view of the Set<CustomAction> as an unmodifiable Collection<Action>. (I hope the return value was unmodifiable already, or that it was a copy having no effect on the original - because if clients have been putting other Actions into it, you're not going to be able to switch to a Set<CustomAction> without breaking client code.)

Here's a quick-and-dirty version:

It may be a bit inefficient to copy to a new array to do this; it would be possible to create a more complex but more efficient adapter class that presents a view of the Set<CustormAction> as a Collection<Action> (unmodifiable of course), with no copying. In fact it might be nice to have a generic factory method which could create such an adaptor on the fly, similar to the various factory methods currently in Collections. No big deal though; for now it's enough to know that it's possible in principle at least.

Now, that covers how to fix the problem after your API has already been released to the world. On the other had if, before the API is released to clients, you spot the possibility that you might want to opt for a more specific collection later - then it does seem as though maybe there's a possible use declaring getActions() thus:

By doing this, the compiler will at least dissuade clients from trying to insert things into the collection. (And unmodifiableCollection() enforces this more rigorously at runtime.) Furthermore if we do get around to using a Set<CustomAction> later internally, this method won't need to change at all. So yes, there does seem to be some use to putting a wildcard here. And techically this method is not a generic method; it's a method with a wildcard for a return type. (It uses a type argument, but no type variable; hence it's not a generic method.) That may or may not fall under what Rick meant I suppose, but it does seem to be a decent counterexample to what he said.
[ August 15, 2005: Message edited by: Jim Yingst ]
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
David, this statement is in complete agreement with the purpose of the wild card as not being "hiding" methods.

On one hand, one parametric class can be used as either covariant, contravariant, or bivariant by attaching a variance annotation�which can be either +, -, or *, respectively�to a type argument.

Do you recall any facility for marking types covariant, contravariant or bivariant? There are none, that is to say you cannot stipulate that a given parameter is write-only, read-only or read-write.

On the other hand, the type system prohibits certain method/field accesses through variant parametric types, when those accesses can otherwise make the program unsafe...

This describes the contravariance that must be introduced by the compiler to retain type safety. That's what we have in java. Exploitation of a variant parameteric reference type is possible but it's not the purpose of wild cards it's a side effect.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Jim and Ilja,

The example given was very contrived. You can't populate a list without using the singletonList, for example. Having said that, I do have an alternative (removed the non-relevant parts of Ilja's example):



This is not a singleton nor is it immutable but that's because there's no concrete implementation of an immutable list or a singleton outside of the Collections.singletonList() call. Immutable lists are trivial to create as would be a singleton: see AbstractList. Replace ArrayList with, say, SingletonList and you have precisely what Ilja was looking for.
[ August 15, 2005: Message edited by: Rick O'Shay ]
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
My point with the example was not the specific functionality, but simply the desire to have the list being able to reference different kinds of lists.

Unfortunately, I don't have a real life example at hand, because we are not yet allowed to use Tiger at work, but here is another contrived one:

 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Rick O'Shay:

I would also add the following. A new requirement comes in. The solution requires a method call through the wild card reference. It won't compile because it accepts a type parameter. UPSADAISY! You need a specific type reference. So much for change insulation.



A new requirement comes in. The solution requires that under certain circumstances, the list is of a more specific type. It won't compile because you haven't use a bounded wildcard (or whatever it's called). So much for change insulation.

My point: both solutions protect against one kind of change, and increase the impact of another kind of change. Which solution is better depends on what future we foresee.
 
Rick O'Shay
Ranch Hand
Posts: 531
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Consessions:

David's point, although not the purpose of generics, is a legitimate use. C++ has this pegged beautifully with the const attribute. Using Java's parameterized type checking, with no explicit marking of contravariant, isn't something I would recommend if asked for an opinion on the matter.

Ilja's example is rather contrived and easily worked around, but it's a valid point that the wild card might be convenient, even optimal, in some cases. So I'll give him that.

Also, I'm switching to decaf to improve argument presentation quality.
 
Don't get me started about those stupid light bulbs.
reply
    Bookmark Topic Watch Topic
  • New Topic