Java Gets Templates
lastberserker writes "Call them all you want - generics, parametrized types, thingamagic mumbojumbo - but (tada!) Java gets templates in 1.5 release. Nice landing after 5+ years of dancing around a bush. Competition is good, pardon my pun."
or something.
that that is is that that is not is not
You know, I've never felt a need for templates. You're the one controlling what data goes into which data structure, so as long as you're not confused (and not forced to work with people who are) templates aren't necessary.
Oh, and I was thinking of another kind of templates (web pages/content/presentation) when I read the title, but of course we have waay too many of those in Java already.
So, what else is still missing from the language? It seems almost ready to go mainstream.
Does this mean Java will get operators soon? Back when Java was introduced, lack of templates was called a "feature", just like the lack of operator overloading. Have the designers seen the light?
Sad to see that the suggested implementation of templates does not include type-specific elements, a-la Eiffel (so, for example, I could declare a SortedList type, where only types that implement Comparable can be used). Java's single-root nature would mean that if you want to accept any type as a parameter, just use Object.
- Tal Cohen
Y'know, just as I'm starting to understand why developers, corporations, every man under the sun thinks Java is a good thing ... they go and do this.
How're you supposed to get people to maintain 1.5 code then? D'you know how many C++ coders don't understand templates? Even to use them it's a scarily small number.
There's very little you can do with a template that you can't do with a well designed virtual base class (whatever they're called in Java). And what you can do boils down to performance, which sucks under Java anyway.
Don't get it. Should've let it be.
Dave
I write a blog now, you should be afraid.
After years of discussions, Sun aknowledges that, although life is possible without generics, it is often better with them.
Now, how many years do we have to wait until they do the same with the good old enum type?
Pascal
<irony> 1+1=3 for large enough values of 1 is the most I can concede in operator deviant behaviour. </irony>
If at first you don't succeed, skydiving is not for you
Doing mainly C++ development I've learned to love the STL and most recently the Boost libraries and I've long regretted that Java didn't support templates. It always confused me that with as much emphasis was placed on strong typing in Java that there was so much need for casting.
Although I'm glad to see the introduction of templates, I'm also concerned about how some of the problems I've encountered with C++ templates will be avoided. The biggest problem I've encountered has been the lack of interface checking.
For example the templated function for_each(begin, end, function) iterates from 'begin' to 'end' calling 'function' on each item inbetween. However there is no mechanism to specify that the type of passed to 'begin' and 'end' must have an equality operator and an operator* and 'function' must take one parameter of the type returned by *begin. This can cause very strange compiler errors when a user tries to pass a variable that doesn't support the proper operations to be compatible with the function. These errors actually appear to occur within the templated code, not the user-written code and can make determining the cause of these errors very difficult.
The Boost Concept Check library provides a way for programmers writing templated C++ code to enforce the interface requirements on the templated parameters in a way that gives more informative error messages. However this is rarely used, even in the Boost libraries. The documentation on the Boost Concept Check gives a good example of the kind of errors that C++ programmers dread.
In order for Java templates to be successful they will need to provide some manner of enforcing requirements on the interface necessary for the templated code to work and provide useful errors at compile time if it doesn't.
Sorry, but I'm going to rant now, because every time I see this ill-informed argument it really annoys me.
Operator overloading is a fact of life when you are working with generics. Suppose I want to write a generalised summation method, which adds up all the elements in a list, whether those are int, float, SomeComplexNumberType or whatever. Question: what well-known and easily understood notation would make sense to use when writing this method? Should I write another method called add that has several versions, for each type I'd like to add up? Or maybe it should be addTo? After all, by your own argument, verbose and descriptive names are the way forward. But what if someone else provides a fixed-point type with a method called sum instead? Or maybe a static method taking two fixed-point values called sumOf? Do you see my point here? If everyone uses + to mean +, then writing such code is easy. If everyone uses different notation, which is forced if you don't allow operator overloading, then what you get is unmaintainable crap that cannot take advantage of generics in many of the most useful ways.
Java already has operator overloading anyway. You have + to concatenate strings. You have a conversion operator called toString. You have / that divides real numbers, but takes the integer part of the result for integral types. The only difference between these and what you have in a language like C++ is that in Java, you can only take advantage of this concept where the Java designers want you to, whereas in C++, you can write your own types at full strength as well.
Yes, operator overloading can be abused. So can method naming (and no, you can't smack the developer on the head, if he happens to work on the other side of the world and you're stuck using his library). But op overloading is more than mere syntactic sugar, where generic programming is involved. There are accepted standards for how we write certain concepts such as addition or strict ordering, and operator overloading is necessary to allow user-defined types to meet those standards, and thus to allow well-written generic code to take advantage of parameterised types and such to best effect. Without it, you've got a fabulous canvas, yet nothing but coarse paintbrushes to draw with.
Alas, as is so often the case, I fear the Java community has been too quick to think it understands, and to go for the buzzword feature without investigating the supports deeply enough. Parameterised types are a good start, and I'm glad to see them included, but I suspect that with experience, there'll be some serious enhancements in a couple of revisions' time...
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
Since no-one has yet pointed it out that I can see, it should be noted that what we're talking about here is parameterised types, and to a limited extent, parameterised methods. This is still quite some way from what you can do with serious generics in other languages.
There is an obvious comparison with C++ templates here, and although those are not the best generics around themselves, they do have a few very useful tricks up their sleeves. In Java, you can now do parameterised containers, and to a limited extent you can do generic algorithms, although the lack of things like operator overloading cripples these from birth. You can't, AFAICS, do some of the funky stuff with traits classes, and things like template metaprogramming, expression templates and Andrei's tricks combining templates and inheritance -- the stuff that's used in several of the best C++ libraries today to give them an edge -- are out of the question for now.
That's a shame, because one of the great things about C++ templates is that using them is pretty easy, but there's quite a bit of power under the hood for people actually implementing class and function templates, such as the designers of those libraries. That means improved performance and/or flexibility for Joe Developers everywhere, even if only a few people know and use the features under the hood.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
But sometimes the terse name is the right one to use. The standard examples come from maths programming. If you have a Complex number class, then it is far more natural to write
Complex c1;
Int i;
Complex c2 = c1 + i;
Rather than
Complex c2 = c1;
c2.addInt(i);
Of course a method is usually the best place to put functionality. But not always Blitz is a great maths library using templates and operator overloading when necessary to produce a very powerful and usable system.
Other examples are the iostream library and STL in C++.
The ability to overload the <operator allows the STL to use primitive types and user defined types in the same algorithms.
Too late:
Sun ignored requests from the developer community to extend the Java core language. Now they finally listen, after Microsoft copied Java with C# and added generics. This is why Java has gone from the hottest thing since Starbucks, to yet another language. They didn't listen to their user base until 2 years after it was too late.
Lessons learned:
This is also why Microsoft is so successful - they constantly innovate, rather than sitting on their market dominance. I foresee Sun suddenly trying to play catch-up with new core Java features. I will enjoy reading the spin Sun puts on this, after saying for years that none of these features were necessary.
You believe M$ propaganda, all that innovation hoo-haa? Sorry pal, wake up, no company that "constantly innovates" comes out with a Java clone about 5 years too late!
Templates only work on Objects - you cannot use templates on built-in types (int, double, etc) unless you manually box/unbox them.
Casts from Object, although hidden from the programmer, are still performed at runtime behind the scenes. This incurs runtime overhead as Java casts invoke code to check the type. This makes Java generics unsuitable for high-performance scientific programming.
> So, what else is still missing from the language? It seems almost ready to go mainstream.
... I think this is relieved a little bit by generics)
...
Well, I don't know about most Java programmers, but the missing features that constantly bug me are:
- Higher-order functions (Seriously, you can't live in a language that doesn't have this once you get used to programming with them!)
- Algebraic Types and Pattern Matching (Vital for manipulating syntax trees, like in a compiler)
- Tuples
- Getting rid of 'null' (This is possible, in fact, easy, and would make the language much cleaner, less error prone, and even more efficient)
- Uniform typing discipline (ie, int vs. Integer
- Parameterized Modules (a la SML's functors)
There are also some bugs in the language itself, though those wouldn't really bother me day-to-day.
- Arrays are covariant, leading to class tag checks on every array write (in the general case)
- The description of binary compatibility is broken (It can lead to situations where recompliation of the class files either leads to a different program, or to a failure to compile!)
-
A language that makes some of these strides is called Nice , though I've never used it.
Recent versions of GCC ship with a version of the Boost Concept Checks embedded in (and adapted to) libstdc++. You can turn it on with a #define, or turn it on all the time with a switch to the 'configure' script. The docs explain how.
My own copy of GCC has the checks turned on permanently. Very useful.
You cannot apply a technological solution to a sociological problem. (Edwards' Law)
Don't forget to visit the Adding Generics Forum for more detailed posts about generics and other new features being added to Java.
What a fool believes, he sees, no wise man has the power to reason away.
Perhaps my terminology isn't the norm in the Java world, but nonetheless, what you have is exactly the same as a conversion operator in other languages. It is a means of implicitly converting between types. Except that in Java, you can only set that up for conversion to a String implicitly. (Don't even get me started on why all classes should have to provide this method in the first place, or on the common root class flamefest.)
Really? What's a class, then?
Lisp has an accepted standard of its own: (+ 1 2) is the sum of the numbers 1 and 2. The point is not a universal notation, but a consistent notation within the language, which allows you to write a generic method once that can work with any suitable type without further modification.
Your example is particularly ironic because Lisp's S-expressions are a prime example of exactly the sort of uniform syntax I'm advocating here.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
For the impatient:
The preliminary spec for generic types in Java is here.
The Sun prototype compiler can be downloaded here.
And a forum for discussion of Java generics is also available.
You might also want to check out CodeGuide. This is AFAIK the only IDE which already supports Java generics as described in the spec (and is an awesome IDE for traditional Java as well).
Enjoy!
-- kryps
- Templates only work on Objects - you cannot use templates on built-in types (int, double, etc) unless you manually box/unbox them.
Remember, generics are just one addition. One of the other additions is autoboxing, so you don't have to worry about manually boxing primitives anymore.There's a bit at the end of this article.
It doesn't mention this specifically, but it alludes to also including static imports, foreach-like construct, and possibly enums.
My objection isn't just that primitive types and UDTs work differently, although that is a fundamental cock-up that should long since have been fixed. My main objection is that we already have a sensible way to represent these concepts, and it's absurd to go around reinventing the wheel, and in a somewhat arbitrary way at that, rather than going with the obvious.
Is it really wrong that a multiply-and-add operation on two matrices be written as M = A * B + C? Or that a new date should be calculated with seasonEnd = seasonBegin + seasonLength? Why is it in any way better to write something like:
or any of the zillions of other possible permutations? Do you honestly feel that this is less subject to abuse, or less error-prone?
And why shouldn't my parameterised tree container, and the algorithms that operate over it, be able to work with int and float data, as well as BigNumber and FixedPoint values? Why should I have to write it all twice (at least)?
Operator overloading is much cleaner, reduces the programming effort required to develop parameterised data structures by a factor of at least two, and consequently reduces error rates. The only point I've seen against it that is true is that there is scope for misleading operator overloads to be provided. It isn't hard to see the conclusion, if only you sit down and look at it objectively.
Oh, and the implicit conversion thing relates to the fact that I can convert an object into a string just by context ("some " + myObject + " thing") but I can't implicitly convert to an int for analogous summation purposes, say.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
Const types and methods in Java would give the programmer a strong guarantee about the behavior of a function rather than just a convention that a naive fellow programmer could easily break.
Great. Finally Java gets one of the features people so desparately need. Admittedly, Java is very cleanly designed, but I really find Java programming painful. Templates were one thing that was missing. Macros are another. Does Java have regular expressions these days?
And really...the more of those missing features get added to Java, the more it gets to look like that other object oriented language with a syntax like C's...I believe it is called C++. Yes, I know objects are optional in C++, but they are in Java, too. int, float, etc. are simple types. Java is big, bloated, slow (yes it is) and a pain to program due to missing features. Once the missing features are added, and the speed is brought on par with other languages through compiling to native code, it will be just like C++. It won't be more portable, either. As far as I am concerned, Java is an expiriment, a failed experiment, to be exact. Go Python!!
Please correct me if I got my facts wrong.
It's not that they are necessarily not type-safe, not in the way that the current containers are because you have to perform a potentially wrong downcast to use them.
However, suppose I develop the world's greatest sorting algorithm, and I want to write a generic implementation for it to sort arbitrary sequence containers of arbitrary types. There are inevitably certain minimal requirements: I must be able to traverse my container in a suitable way for my algorithm to work, the types must support a suitable ordering relation, and so on.
As I understand it, several people in this thread are advocating the use of interfaces to ensure that only suitable types get passed to my sorting algorithm, thus ensuring type safety. The problem with that is what happens when someone comes along with a new container or sortable type that provides the necessary methods, but doesn't explicitly use that interface. Now my "generic" sorting algorithm can't be used, purely because of a naming issue. (I realise there are certain standard interfaces defined for things like comparison in Java, but obviously for types and algorithms in general that won't be the case.)
The requirement to implement interfaces explicitly is simply an unnecessary bar to the use of generics. In other languages, from C++ to SML, you can achieve similar effects without ever explicitly naming your interface. Instead, the algorithm simply fails to compile if it's used with types that don't match the way the algorithm uses them. Added to the presence of operator overloading, which standardises many fundamental arithmetic and logical operations from the start, writing generic algorithms that really are quite general is significantly easier.
Further, the concept of an interface as defined in languages like Java is inadequate to enforce useful constraints in practice anyway. The suggestion has been made, and refuted, many times in the C++ world, and alternative approaches to document and enforce constraints within template code have been developed instead.
The only alternative that I see as Java's generics stand today is to define all your generic algorithms to work with any Object, and go back to downcasting, but then you might as well never have bothered with generics in the first place.
Thus your two alternatives are either an interface-based system, which is type-safe but unnecessarily restrictive and less efficient, or to use generics only for type-safe containers and to write your generic algorithms in terms of Objects, which leaves open type safety loopholes.
Ultimately, implementing generic algorithms will always suffer because one man's search method is another man's find. It is helpful to offer methods of standard names to make generalising code easier, but clearly you can't have a standard interface for everything in a general purpose library that may be put to any use. I advocate the use of operator overloading, because the sorts of calculations performed by built-in operators tend to be useful in a wide variety of contexts. Beyond that, if your generics only deal with the methods that are actually presented, whether or not they're explicitly identified by a formal interface, at least you keep the range of application as wide as possible. If you need to go farther still, you're into the land of intermediate classes (such as the iterators in C++'s standard library) to standardise an interface by force. In that case, you can always implement a named interface there, but of course there's an inherent performance hit in doing so, and you really want such intermediate classes to be so lightweight that you don't even realise they're there under normal use.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
I'm a mathematician. I do arithemetic operations with types that aren't built in to the language. And they aren't all exotic -- things like vector addition. (no, not the misnamed java.util.Vector.) If x,y,z are n-dimensional vectors, how to you code w=x+y+z? I want the code to look the way it does for builtins:
Vector w=x+y+z;
Otherwise, the math gets _badly_ obfuscated and buried in minutia. I have yet to use a language that allowed it. (Yes, this is a pet peeve.)
And I don't buy that it won't be clear what "+" means in each case. It means the addition operation that makes mathematical sense given the operands. If that isn't clear to you, you aren't going to get the code no matter how it's written.
Example time. The addition code is written for binary (2 argument) addition, because you shouldn't have to write more than. Assume addition code also allocates the memory or invokes the constructor for the result, because it gets *really* ugly if it doesn't.
C: You have to use a name instead of "+", and you have to manage the memory. I wrote a few thousand of lines of this stuff as a grad student.
double *temp = vectoradd(x,y);
double *w = vectoradd(temp,z);
free(temp);
C++: You can use "+", but you still have to manage the memory. [It's possible to do better if you overload "=" to do reference counting. But if you don't like overloading "+", you really won't like that.]
Vector temp = x+y;
Vector w = temp+z;
~temp();
Java: You don't have to manage the memory, but you are back to using names:
Vector w = x.add(y).add(z);
If these examples don't seem very messy, see what (a*x*x+b*x+c)/(d*x*x+e*x+f) turns into if you change the variables from doubles to some object type -- say representing complex numbers. And remember that that's pretty simple as math expressions go.
Here's what I propose: don't let the programmer overload "+" arbitrarily. But for classes which implement certain kinds of mathematical entities, have a set of methods they must implement with the types that make mathematical sense. E.g. if class foo claims to be a vector, it must implement
public foo add(foo, foo)
public foo scalarMultiply(double, foo)
public foo scalarMultiply(foo, double)
Then overload the standard arithmetic operators with these methods. It should be possible to make it work well for mathematical objects, but unpleasant for anyone who wants to tries to cheat the system.
I wish all the /.ers would think and do a little research for saying the proverbial Java is slow and then shoot themselves in the foot by making a stupid statement.
.NET architecture. So many terms are interchangable with JAVA / J2EE concepts.
AKA -> Does java do regular expressions ? Java applets suck.
Learn a bit about mvc before you comment and say php does everything. If you truely understood all the benefits of o/o programming you would appreciate the abilities to create a set of reusable individual components which could have many clients. Try that in PHP. Id like to see any C programming using a handful of lines be able to consume bytes from a socket and then by changing one line read from a file(java.io.*/java.net.*).
One cant dispute the fact that java is elegant, useful and obviously a success hence its basically the model that sun copied with its
The problem with null is that it's yet another unnecessary run-time check. Suppose having a value of class Integer meant that you really had an integer object (instead of including the possibility that it is null). Sometimes we want to return a value plus the possibility of an error condition, in which case null is useful, so we'd have some other type for "possibly null" objects.
... code involving x ...
...
... break; ... same code involving y ...
You might have something built in to the language like this, like writing "Integer?" as the class name (the language Popcorn does this), you might also just have a powerful enough language that it can be coded up easily (SML has this with its polymorphic 'option' type).
With the old null, you might write this:
Integer x = something();
if (x != null) {
} else {
}
Note that if you pass x to another function, that function probably needs to check to see if x is null, too. Similarly, the bytecode interpreter needs to check for null whenever you do a method invocation on x. Without null, you might write (I am just making up the syntax; you might want to see how Nice does it):
Integer? x = something();
switch (x) {
null:
Integer(y):
}
The important thing here is that we've statically captured the fact that we've done a null check (y would have type 'Integer', not type 'Integer?'). Now, the byte code interpreter never needs to check y to see if it's null (efficiency gain), the programmer is never allowed to use a value unless he's checked that it's null (program robustness), the potential for a function to return null is reflected in its type (modularity), and the programmer has to insert fewer checks, because once it's an Integer, it is not necessary (and doesn't make sense!) to check for nullness (saves typing).
In my own Java programming, and experience with using other folks' Java programs, the Null Pointer Exception is much more common than the Class Cast Exception. However, in my experience with Object Oriented Programmers, the love of "null" runs deep, so it might be a harder sell than generics!
And how, exactly, do you propose to work out who is "cheating the system" and whose use is quite legitimate? Is the use of + to concatenate strings acceptable to you? What about other string-like classes? How about for adding date/time and time period classes?
At the end of the day, you can never anticipate every use to which such a feature could usefully be put. You have two choices: try, and provide something that is sometimes useful but usually frustrating, or leave the choice to the informed developer, and let them do what they will with it. The fact that C++ is still here is testament to the fact that the second approach works. I can't think of a single language that tries to restrict the programmer this way that has lasted more than a short period.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
I've already included string concatenation with + -- the set of all finite strings from a given alphabet forms a semigroup under concatenation.
But you're basically right. I don't know how many mathematical types -- or properties -- I would want to add. It would probably be a few dozen, and undoubtedly more in the second release.
I don't want my languages telling me what to do any more than you do. But that's not where I'm going. I need both overloading and automated memory management, and I need them in a compiled general purpose OO language. But I only need overloading in a specific context where it really makes sense. I'd be quite happy with a design-by-contract setup which requires that the class fulfill (within roundoff error) the appropriate mathematical definition. E.g. to be a semigroup, you have to have the operator be associative. That is, (a+b)+c == a+(b+c).
And that shouldn't trigger the (legitimate, IMO) complaints that overloading-heavy code is write-only.
I think we're on the same wavelength, but just a couple of points are worth mentioning...
Firstly, it's almost invariably not true that (a+b)+c == a+(b+c) from a numerical analysis point of view. The problems of limited precision render such mathematical niceties, well, niceties. While I appreciate that it would be nice to program in a higher level language where such mathematical contracts were used, none of those under discussion around here will ever qualify.
As for overloading-heavy code being write-only, I couldn't disagree more. Code where operators are overloaded usefully (viz. typical mathematical types, string concatenation, etc.) is far more readable than the equivalent with longhand methods being used all over the place, particularly in a language such as Java that has no free functions. The only time such code becomes write-only is when the operators are overloaded in a misleading way (for example, having unexpected side-effects) and that's no different to the effect of choosing a bad name for any other function, or variable, or type.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
I am aware of roundoff error. That's why there's a parenthetical "within roundoff error" in my previous message. :)
I don't have a much experience reading other people's code with overloaded operators. But I think overloading would help clarity if, and only if, the overloaded function is enough like the builtin version of the operator. At a minimum, the semantics need to be analogous and the presence or absense of side-effects needs to be the same. Overloading "=" to implement reference counting doesn't qualify in my book. (This is closer to what you just said than to what I initially proposed.)
After all, in many languages the builtins are essentially overloaded -- int+int isn't the same thing as double+double.
That is one of the good things on the Java coding guidelines. Names are verbose. There's no strcmp. There's String.compareTo.
Exactly. To create a file, I say createNewFile(). To create a folder, I say mkdir(). Er, oops. I guess Sun let one of their old Unix hackers out of his cage while they were designing Java.
How precisely is java's new generics design flawed?
Look at processors. Sun leapfrogged HP, DEC and IBM with Sparc way back when. HP came back and did it to all of them; now IBM. In technology, there is always a feature/power/flexability leapfrog cycle where one vendor (including MS) pulls out ahead, and then later on another does the same. No news there...
we speak the way we breathe --Fugazi
It looks like you're right and I was wrong.
It's been a while, but I believe I was manipulating pointers to objects to avoid all copying. The application was computationally intensive, but it was probably silly of me anyway.
My C++ experience isn't extensive, and the implementation wasn't exactly modern.