java7新特性之Simplified varargs method invocation
This is one of the simplest changes of all—it moves a warning about type information
for a very specific case where varargs combines with generics in a method signature.
Put another way, unless you’re in the habit of writing code that takes as arguments
a variable number of references of type T and does something to make a collection
out of them, you can move on to the next section. On the other hand, if this bit of
code looks like something you might write, you should read on:
public static <T> Collection<T> doSomething(T... entries) { ... }
Still here? Good. So what’s this all about?
As you probably know, a varargs method is one that takes a variable number of
parameters (all of the same type) at the end of the argument list. What you may not
know is how varargs is implemented; basically, all of the variable parameters at the end
are put into an array (which the compiler automatically creates for you) and they’re
passed as a single parameter.
This is all well and good, but here we run into one of the admitted weaknesses of
Java’s generics—you aren’t normally allowed to create an array of a known generic
type. For example, this won’t compile:
HashMap<String, String>[] arrayHm = new HashMap<>[2];
You can’t make arrays of a specified generic type. Instead, you have to do this:
HashMap<String, String>[] warnHm = new HashMap[2];
This gives a warning that has to be ignored. Notice that you can define the type of
warnHm to be an array of HashMap<String, String>—you just can’t create any instances
of that type, and instead have to hold your nose (or at least, suppress the warning) and
force an instance of the raw type (which is array of HashMap) into warnHm.
These two features—varargs methods working on compiler-generated arrays, and
arrays of known generic types not being an instantiable type—come together to cause
a slight headache. Consider this bit of code:
HashMap<String, String> hm1 = new HashMap<>();
HashMap<String, String> hm2 = new HashMap<>();
Collection<HashMap<String, String>> coll = doSomething(hm1, hm2);
The compiler will attempt to create an array to contain hm1 and hm2, but the type of
the array should strictly be one of the forbidden array types. Faced with this dilemma,
the compiler cheats and breaks its own rule about the forbidden array of generic type.
It creates the array instance, but grumbles about it, producing a compiler warning
that mutters darkly about “unchecked or unsafe operations.”
From the point of view of the type system, this is fair enough. But the poor developer just wanted to use what seemed like a perfectly sensible API, and there are scarysounding warnings for no adequately explained reason.
WHERE DID THE WARNING GO IN JAVA 7?
The new feature in Java 7 changes the emphasis of the warning. After all, there is a
potential for violating type safety in these types of constructions, and somebody
had better be informed about them. There’s not much that the users of these types of APIs
can really do, though. Either the code inside doSomething() is evil and violates type
safety, or it doesn’t. In any case, it’s out of the API user’s hands.
The person who should really be warned about this issue is the person who wrote
doSomething()—the API producer, rather than the consumer. So that’s where the
warning goes—it’s moved from where the API is used to where the API was defined.
The warning once was triggered when code that used the API was compiled.
Instead, it’s now triggered when an API that has the potential to trigger this kind of
type safety violation is written. The compiler warns the coder implementing the API,
and it’s up to that developer to pay proper attention to the type system.
To make things easier for API developers, Java 7 also provides a new annotation
type, java.lang.SafeVarargs. This can be applied to an API method (or constructor)
that would otherwise produce a warning of the type discussed. By annotating the
method with @SafeVarargs, the developer essentially asserts that the method doesn’t
perform any unsafe operations. In this case, the compiler will suppress the warning.
CHANGES TO THE TYPE SYSTEM
That’s an awful lot of words to describe a very small change—moving a warning from
one place to another is hardly a game-changing language feature, but it does serve to
illustrate one very important point. Earlier in this chapter we mentioned that Project
Coin encouraged contributors to mostly stay away from the type system when proposing changes. This example shows how much care is needed when figuring out how different features of the type system interact, and how that interaction will alter when a
change to the language is implemented. This isn’t even a particularly complex
change—larger changes would be far, far more involved, with potentially dozens of
subtle ramifications.
This final example illustrates how intricate the effect of small changes can be.
Although they represent mostly small syntactic changes, they can have a positive impact
on your code that is out of proportion with the size of the changes. Once you’ve started
using them, you’ll likely find that they offer real benefit to your programs.
读书笔记:The Well-Grounded Java Develope