PROPOSAL: Simplified Varargs Method Invocation

Reinier Zwitserloot reinier at zwitserloot.com
Fri Mar 6 06:58:06 PST 2009


The plot thickens. Javac v1.5 WILL warn you when you de-varargs a  
superclass. However, any javac v1.6 will NOT. Evidently one of the  
javac engineers had a reason to remove the warning. That reason would  
probably be very pertinent to this discussion. Anyone familiar enough  
with mercurial to dig the comment on this changeset out of the javac's  
repository?

Remi - I covered the need to update superclasses without breaking  
implementers, but if you (re)compile a subclass that de-varargses when  
source compatibility mode is 1.5 or up, I can see absolutely no reason  
for javac to be silent about it. It should generate this warning. That  
change to javac 1.6 needs to be undone.

If, as Remi suggests, we treat warnings as effective errors, and thus  
we treat generics-based arrays as something you really shouldn't ever  
use, then we have an entirely different bug: The varargs mechanism is  
then broken. Something as simple as this should then not be something  
you ever ought to do (eventhough in practice it can't go wrong and the  
warning generated by the following code snippet is a 'bug' in that  
there's no way it'll ever break):

listOfLists = Arrays.asList(listA, listB);


If there is no (acceptible) way to let the compiler figure out what is  
and isn't safe, then the only solution I can see is to introduce  
java.lang.VarArgs<T>, and new syntax (4 dots?), or just re-use  
java.util.List<T>.

Either way, I'm still convinced that this is effectively a javac bug,  
or at the very least a severe 'feature' that has caused many tens of  
thousands of manhours of solution-hunting and frustration.

For what it's worth, its my opinion that we can and should figure out  
when varargs methods are perfectly safe, generate no warnings anywhere  
if that happens and all involved code is compiled by javac7 or higher,  
and accept that Bad Things will happen if you varargs a non-reified  
type into a method where a subclass has reified the type (but that  
warning should be restored). After all, Bad Things happen now too, and  
in either situation you get at least 1 warning. However, in the  
proposed solution, the warning is at least moved to the place where it  
is more relevant: The programmer who is reifying that array type.  
Granted, without an additional proposal to rewrite a signature to  
Object[], there's nothing this programmer can do about it, but at  
least he knows what's happening. The current warning on the call site  
does absolutely nothing useful other than suggesting you can't really  
use varargs at all, eventhough (quite literally) in 99%+ of all cases,  
there's absolutely no danger whatsoever.

  --Reinier Zwitserloot



On Mar 6, 2009, at 09:28, Rémi Forax wrote:

> Reinier Zwitserloot a écrit :
>> Heh, that was the next proposal I was going to write.
>>
>> (Apologies for a long and technically complicated post. I've  
>> rewritten  it several times in an attempt to get to the point  
>> faster, but this is  the best I can do).
>>
>> It really pains me to rain on your parade. I was doing some   
>> experimenting and found an admittedly very unlikely scenario that   
>> isn't backwards compatible. Then I realized, that this is actually   
>> (arguably) a bug, or at least a severe misfeature in javac right  
>> now!  Therefore, this proposal should fix this problem as well. Of  
>> all  proposals so far, I rate this one the highest, because it  
>> causes so  much confusion and is closer to a bug in javac than a  
>> language  feature, so I would be very happy if this proposal can be  
>> fully ironed  out.
>>
>> Complete code showing the problem - copy and paste into Main.java,   
>> compile, and run:
>>
>> ------
>>
>> import java.util.*;
>>
>> class Foo<T> {
>> 	public void foo(T... t) {}
>> }
>>
>> class Bar extends Foo<String> {
>>         //de-varargsing? Why is this legal?
>>
> Because you want to be able to replace an array with a varargs in  
> library without
> creating errors in code that use that library.
>> 	public void foo(String[] t) {
>> 		System.out.println(t.getClass().getComponentType());
>> 	}
>> }
>>
>> public class Main {
>> 	public static void main(String[] args) {
>> 		Foo<String> f = new Bar();
>> 		List<String> l = Arrays.asList("a", "b", "c");
>> 		bar(f, l);
>> 	}
>> 	
>> 	public static <T> void bar(Foo<T> f, List<T> l) {
>> 		f.foo(l.get(0), l.get(1), l.get(2));
>> 	}
>> }
>>
>
>> -----
>>
>> The result is an error:
>>
>> Exception in thread "main" java.lang.ClassCastException:   
>> [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
>>
>> Right now, you get the usual varargs warning, but nothing else at   
>> compile time. At runtime, you get the above error.
>>
> Warnings in Java are not like in C, it means something will go  
> wrong :)
>
> the call:
>
> f.foo(l.get(0), l.get(1), l.get(2));
>
> generate a warning because you are creating an arry of parameterized  
> type
> (a Foo<T>[]) which are "inherently unsafe" to quote gilad bracha.
>
>> The real bug here is the ability for any subclass to de-varargs a   
>> parameter.
> No
>
> [...]
>
> See:
>>> Angelika Langer's Java Generics FAQ, "Why does the compiler   
>>> sometimes issue
>>> an unchecked warning when I invoke a 'varargs' method?" (
>>> http://tinyurl.com/8w2dk)
>>>
> Rémi




More information about the coin-dev mailing list