Выбрать главу

So yes, I think it’s important that you learn all this stuff. But do I think you should start with a low-level language like C? No! Students should not have to deal with buffer overruns, manual memory allocation, and the like in their first exposure to programming.

James Gosling once said to me, discussing the birth of Java, “Occasionally you get to hit the reset button. That’s one of the most marvelous things that can happen.” Usually, you have to maintain compatibility with stuff that’s decades old; rarely, you don’t, and it’s great when that happens. But unfortunately, as you can see with Java, it only takes you a decade until you’re the problem.

Seibeclass="underline" Since you say that, is Java off in the weeds a little bit? Is it getting more complex faster than it’s getting better?

Bloch: That’s a very difficult question. In particular, the Java 5 changes added far more complexity than we ever intended. I had no understanding of just how much complexity generics and, in particular, wildcards were going to add to the language. I have to give credit where credit is due—Graham Hamilton did understand this at the time and I didn’t.

The funny thing is, he fought against it for years, trying to keep generics out of the language. But the notion of variance—the idea behind wildcards—came into fashion during the years when generics were successfully being kept out of Java. If they had gone in earlier, without variance, we might have had a simpler, more tractable language today.

That said, there are real benefits to wildcards. There’s a fundamental impedance mismatch between subtyping and generics, and wildcards go a long way towards rectifying the mismatch. But at a significant cost in terms of complexity. There are some people who believe that declaration-site, as opposed to use-site, variance is a better solution, but I’m not so sure.

The jury is basically still out on anything that hasn’t been tested by a huge quantity of programmers under real-world conditions. Often languages only succeed in some niche and people say, “Oh, they’re great and it’s such a pity they didn’t become the successful language in the world.” But often there are reasons they didn’t. Hopefully some language that does use declarationsite variance, like Scala or C# 4.0, will answer this question once and for all.

Seibeclass="underline" So what was the impetus for adding generics?

Bloch: As is always the case for ideas that prove less wonderful than they seemed, it was believing our own press sheets. My mental model was, “Hey, collections are almost all homogeneous—a list of strings, a map from string to integer, or whatever. Yet by default they are heterogeneous: they’re all collections of objects and you have to cast on the way out and that’s nonsense.” Wouldn’t it be much better if I could tell the system that this is a map from strings to integers and it would do the casting for me and it would catch it at compile time when I tried to do something wrong? It could catch more errors—it would have higher-level-type information and that sounds like a good thing.

I thought of generics in the same way I thought about many of the other language features we added in Java 5—we were simply getting the language to do for us what we had to do manually before. In some cases I was dead on: the for-each loop is just great. All it does is hide the complexity of the iterators or the index variables from you. The code is shorter and the conceptual surface area is no larger. In a sense, it’s even smaller because we’ve created this false polymorphism between arrays and other collections so you can iterate over an ArrayList or an array and not know or care which you’re iterating over.

The main reason this thinking didn’t apply to generics is that they represent a major addition to an already complex type system. Type systems are delicate, and modifying them can have far-reaching and unpredictable effects throughout the language.

I think the lesson here is, when you are evolving a mature language you have to be even more conscious than ever of the power-versus-complexity trade-off. And the thing is, the complexity is at least quadratic in the number of features in a language. When you add a feature to an old language you’re often adding a hell of a lot of complexity. When a language is already at or approaching programmers’ ability to understand it, you simply can’t add any more complexity to it without breaking it.

And if you do add complexity to it, will the language simply disappear? No, it won’t. I think C++ was pushed well beyond its complexity threshold and yet there are a lot of people programming it. But what you do is you force people to subset it. So almost every shop that I know of that uses C++ says, “Yes, we’re using C++ but we’re not doing multiple-implementation inheritance and we’re not using operator overloading.” There are just a bunch of features that you’re not going to use because the complexity of the resulting code is too high. And I don’t think it’s good when you have to start doing that. You lose this programmer portability where everyone can read everyone else’s code, which I think is such a good thing.

Seibeclass="underline" Do you feel like Java would be better off today if you had just left generics out?

Bloch: I don’t know. I still like generics. Generics find bugs in my code for me. Generics let me take things that used to be in comments and put them into the code where the compiler can enforce them. On the other hand, when I look at those crazy parameterized-type-related error messages, and when I look at generic type declarations like the one I wrote for Enum—class Enum<E extends Enum<E>>—I think it’s clear that the generics design wasn’t quite mature enough to go in.

We’re all optimists in our profession or we’d be forced to shoot ourselves. So we say, “Oh, yeah, of course we can do this. We’ve known about generics since CLU. This is 25-year-old technology.” These days you hear the same argument applied to closures except it’s 50-year-old technology. “Oh, it’s easy; it doesn’t add any complexity to the language at all.”

Hell yes, it does. But I think many of us have learned from our experience with generics. You shouldn’t add something to a language until you really understand what it’s going to do the conceptual surface area—until you can make a convincing argument that working programmers will be able to use the new feature effectively, and that it will make their lives better.

If you look at how the man on the street has been reacting to generics, we certainly should have done something other than what we did. Does that mean we shouldn’t have done generics at all? No, I don’t think so. I think that generics are actually good. The fundamental argument that most collections are homogeneous, not heterogeneous, so it should be easy to deal with homogeneous collections is true. Furthermore casting is generally a bad thing. Casts can fail and casts don’t make your program beautiful. So I think you should be able to say what kind of collection it is and then it should just automatically be enforced for you. But does that mean you have to suffer with all this complexity that we have today? No. I think we just didn’t take the right cut at it.

Seibeclass="underline" Was there real user pressure for generics? Were people complaining that the lack of generics was stopping them from writing software?

Bloch: Were real engineers bitching about the lack of generics? I think the unfortunate answer to that question is, no, they weren’t. I think I was guilty of putting in something because it was neat. And because it felt like the right thing to do.

That said, a lot of engineering is from the gut. Had people been telling me to put in foreach? No. They hadn’t been telling me to do that either. But I just knew that it was the right thing to do. And I was right—everybody likes it.

But I think a big sin in our area, in engineering, is doing stuff just because it’s neat, because it’s good engineering, whatever. If you’re not solving real problems for real users—in this case, Java programmers—then you shouldn’t add the feature.