• No results found

Simplified varargs method invocation

This chapter covers

17The changes in Project Coin

1.3.6 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 “diamond syntax” name

This form is called “diamond syntax” because, well, the shortened type information looks like a diamond. The proper name in the proposal is “Improved Type Inference for Generic Instance Creation,” which is a real mouthful and has ITIGIC as an acro- nym, which sounds stupid, so diamond syntax it is.

18 CHAPTER 1 Introducing Java 7

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 devel- oper just wanted to use what seemed like a perfectly sensible API, and there are scary- sounding warnings for no adequately explained reason.

WHEREDIDTHEWARNINGGOIN 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 bet- ter 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.

CHANGESTOTHETYPESYSTEM

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 propos- ing changes. This example shows how much care is needed when figuring out how dif- ferent 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.

19 Summary

1.4

Summary

Making changes to the language itself is hard. It’s always easier to implement new fea- tures in a library (if you can—not everything can be implemented without a language change). The challenges involved can cause language designers to make smaller, and more conservative, changes than they might otherwise wish.

Now, it’s time to move on to some of the bigger pieces that make up the release, starting with a look at how some of the core libraries have changed in Java 7. Our next stop is the I/O libraries, which have been considerably revamped. It will be helpful to have a grasp of how previous Java versions coped with I/O, because the Java 7 classes (sometimes called NIO.2) build upon the existing framework.

If you want to see some more examples of the TWR syntax in action, or want to learn about the new, high-performance asynchronous I/O classes, then the next chap- ter has all the answers.

20

New I/O

One of the larger API changes in the Java language—a major update to the set of

I/OAPIs, called “more New I/O” or NIO.2 (aka JSR-203)—is the focus of this chapter.

NIO.2 is a set of new classes and methods, that primarily live in the java.nio package.

It’s an out-and-out replacement of java.io.File for writing code that inter-

acts with the filesystem.

■ It contains new asynchronous classes that will allow you to perform file and network I/O operations in a background thread without manually configur- ing thread pools and other low-level concurrency constructs.

■ It simplifies coding with sockets and channels by introducing a new Network- Channel construct.

This chapter covers

■ The new Java 7 I/O APIs (aka NIO.2)

■ Path—the new foundation for file- and directory-based I/O

■ The Files utility class and its various helper methods

■ How to solve common I/O use cases

21 New I/O

Let’s look at an example use case. Imagine your boss asked you to write a Java routine that went through all the directories on the production server and found all of the properties files that have been written with a variety of read/write and ownership per- missions. With Java 6 (and below) this task is almost impossible for three reasons:

There is no direct class or method support for navigating directory trees.There is no way of detecting and dealing with symbolic links.1

It’s not possible to read the attributes (such as readable, writable, or execut-

able) of a file in one easy operation.

The new Java 7 NIO.2 API makes this programming task much more possible, with direct support for navigating a directory tree (Files.walkFileTree(), section 2.3.1), symbolic links (Files.isSymbolicLink(), listing 2.4) and simple one-line operations to read the file attributes (Files.readAttributes(), section 2.4.3).

In addition, your boss now wants you to read in those properties files without inter- rupting the flow of the main program. You know that one of the properties files is at least 1MB in size; reading this will likely interrupt the main flow of the program! Under Java 5/6, you’d likely have to use the classes in the java.util.concurrent package to create thread pools and worker queues and read this file in a separate background thread. As we’ll discuss in chapter 4, modern concurrency in Java is still pretty difficult and the room for error is high. With Java 7 and the NIO.2API, you can read the large file in the background without having to specify your own workers or queues by using the new AsynchronousFileChannel (section 2.5). Phew!

The new APIs won’t make you a perfect cup of coffee (although that would be a nice feature), but they will be extremely useful because of major trends in our industry.

First, there is a trend to explore alternative means of data storage, especially in the area of nonrelational or large data sets. This means the use case for reading and writ- ing large files (such as large report files from a microblogging service) is likely to come up in your immediate future. NIO.2 allows you to read and write large files in an asynchronous, efficient manner, taking advantage of underlying OS features.

A second trend is that of multicore CPUs, which open up the possibility for truly concurrent (and therefore faster) I/O. Concurrency isn’t an easy domain to master,2

and NIO.2 offers a large helping hand by presenting a simple abstraction for utilizing multithreaded file and socket access. Even if you don’t use these features directly, they will have a large impact on your programming life as IDEs and application servers, and popular frameworks will utilize them heavily.

These are just some examples of how NIO.2 can help you. If NIO.2 sounds like it solves some of the problems you’re facing as a developer, this chapter is for you! If not, you can always come back to the sections in this chapter that deal with Java I/O

coding tasks.

1 A symbolic link is a special type of file that points to another file or location on the file system—think of it as

a shortcut.

22 CHAPTER 2 New I/O

This chapter will give you enough of a taste of Java 7’s new I/O capabilities for you to start writing NIO.2-based code and confidently explore the new APIs. As an addi- tional benefit, these APIs use some of the features we covered in chapter 1—proof that Java 7 does indeed eat its own dog food!

TIP The combination of try-with-resources (from chapter 1) and the new

APIs in NIO.2 make for very safe I/O programming, probably for the first time in Java!

We expect that you’ll most likely be wanting to use the new file I/O capabilities, so we’ll cover that in the greatest detail. You’ll begin by learning about the new filesystem abstraction, Path, and its supporting classes. Building on top of Path, you’ll work through common filesystem operations such as copying and moving files.

We’ll also give you an introduction to asynchronous I/O and look at a filesystem- based example. Lastly we’ll discuss the amalgamation of Socket and Channel func- tionality and what that means for developers of network applications. First, though, we’ll look at how NIO.2 came about.