Verbosity is not Java’s fault
schneide.wordpress.com
schneide.wordpress.com
It isn't the author's fault, but reading articles like this make me feel like Java supporters want it both ways. They point to Java's tooling and vast herds of libraries as benefits, but when you point out flaws in some implementation they say the language isn't the fault, it's that particular library.
On the other side, when criticizing languages like Ruby they are quick to point to practices like monkey-patching as examples of how Ruby permits abuse in practice.
Well, which is it? Do we compare languages on the basis of the actual libraries and programs people write? Or do we compare them on the basis of the libraries and programs they could write if they took advantage of the language's features and also had a keen sense of design?
It's both. A Language-with-a-capital-L is the libraries it includes, the libraries it allows, and the libraries it makes unnecessary.
Java's verbose APIs are Java's fault, because Java doesn't provide metaprogramming tools to make the boilerplate go away.
Some stuff is just plain verbose, in part due to lack of type inference -
EndlessParametrizedType foo = new EndlessParametrizedType()
As to the APIs, some of it is due to excessively baroque APIs. But even with a well-designed API, in order to provide a reasonable amount of flexible functionality, a lot of the internal structure of the implementation is often exposed so it can be accessed. This is a consequence of static typing - in a dynamic language, you're more likely to just reach into the bowels of an object and pull out, change or replace bits you want. This isn't an option in Java and a flexible API ends up being a composition of a lot of smaller APIs. In the standard library, it's often left to others to provide a simple layer on top that covers the base cases - for instance, Apache Commons mail for Java Mail.Note: I'm not necessarily saying that I agree with the author.
CLOS:
(defclass class () ((foo :reader get-foo :initarg :foo)))
Moose: class Class {
has 'foo' => ( is => 'ro' );
}
Java: class Class {
private String foo;
public Class(String foo) { // named initargs are clearly for losers
this.foo = foo;
}
};
Notice how a common high-level task requires a manual error-prone
implementation, every single time. Java provides only the concepts of
"class", "field" and "constructor", while the other languages provide
the high-level concept of an "attribute", which is what the programmer
actually wants. (The first concept is easy for the language
implementor, the second concept is easy for the practicing
programmer.)I should also point out how the verbosity exists only for the sake of being verbose, not for creating more reliable software. Perl and CLOS assume I want named initargs, making use of my class self-documenting:
(make-instance 'class :foo 42 :bar 123)
Class->new( foo => 42, bar => 123)
That's verbose, but it makes the code very easy to read and write. No
guessing about what slot is being set to what value.With Java, though, it's anyone's guess as to what's going on:
new Class(42, 123);
The only way to know which field gets set to which value is to Read The Fine Source: public Class(int bar, int foo){
this.foo = bar;
this.bar = foo;
System.Utils.delete_users_pr0n_stash();
}
foo does get set to 42, but only by accident... and there are unexpected side effects.So much less typing though! cough.
Another common programming pattern is reusing code. (I hear people like doing that.) Say you have classes representing numbers, and you want to add a "not equals" method to each type. That's easy, you can just write a generic role like:
role Eq {
requires 'equal';
method not_equal($a: $b){
return !$a->equal($b);
}
}
Then I can easily reuse that extremely-complex "not_equal" method: class Int with Eq {
has value => ( is => 'ro', required => 1 );
method equals($a: $b){
return $a->value == $b->value;
}
}
Int->new( value => 42 )->not_equals( Int->new( value => 123 ) ); # True
In Java, I can still maintain a similar interface: interface Eq {
public bool equal(Eq that); // Eq is not really the type I want, but whatever.
public bool not_equal(Eq that);
}
But the implementation can't be shared: class Int implements Eq {
private int value;
public Int(int value){ // didn't we just say "int value" like one line ago?
this.value = value;
}
public bool equal(Int that){
return this.value == that.value;
}
public bool not_equal(Int that){
return !this.equal(that);
}
}
This is both verbose and error-prone. (I will also point out that
Java calls Eq "Comparable" and uses the "implements" keyword instead
of "with". But this is petty. I'm surprised they were happy with
"implements" and didn't choose the clearer expression
"implementsTheInterfaceThatIAmAboutToTypeRightAfterIAmDoneTypingThis".
Your editor can type that for you, after all...)But wait, we can use the strategy pattern to save us!
class NotEqualStrategy {
public bool not_equal(Eq a, Eq b){
return !a.equal(b);
}
}
class Int {
private NotEqualStrategy not_equal_strategy;
private value;
public Int(NotEqualStrategy nes, int value){
this.not_equal_strategy = new;
this.value = value;
}
public bool equal(Int that){
return this.value == that.value;
}
public bool not_equal(Int that){
return not_equal_strategy.not_equal(this, that);
}
}
At least I don't have to cut-n-paste the logic anymore, but it's still
very verbose. Imagine you had to delegate more than one method -- you
can do it, but it involves a ton of meaningless code.In Perl, I can just say:
class NotEqualsStrategy {
method not_equal(Eq $a, Eq $b){
return !$a->equal($b);
}
}
class Int {
has 'not_equals_strategy' => (
is => 'ro',
isa => 'NotEqualStrategy',
required => 1,
handles => ['not_equal'],
);
has 'value' => ( is => 'ro' );
method equal(Int $a: Int $b){
$a->value == $b->value;
}
}
Notice how I didn't have to type the cut-n-paste delegation; the
language did it for me. I didn't have to type it, I don't have to
look at it, and I can't accidentally fuck it up. That's what
programming languages are supposed to do for you, and that's what Java
doesn't do.In conclusion, Java is verbose, and verbosity is harmful.
> In Perl, I can just say:
Moose is an OOP framework built on top of Perl, comparing it to out-of-the-box Java code is quite misleading and I'm not sure what point it proves? It's like saying MVC in PHP is verbose compared to Django or Rails rather than Python or Ruby. Using MooseX::Declare is even more misleading. Does that even work on Windows yet?
MooseX::Declare has always worked on Windows. If you can compile Perl, you can compile Devel::Declare.
Regardless, MooseX::Declare does not change the verbosity of the code:
use MooseX::Declare;
class Foo {
has 'bar' => ( is => 'ro' );
}
package Foo;
use Moose;
has 'bar' => (is => 'ro');
The MX::D version is one line longer :P.I mean, in Java can you do this?
class MyClass {
private String foo;
public Class(String foo) {
this.foo = foo;
}
}
var inst = new MyClass();
inst._foo = 'I just broke your encapsulation.';
Because you can with psuedo-privacy in Perl and Moose.The other thing is that OOP in Perl without Moose is even more verbose than Java. I will never get over having to write my own new subroutine :)
P.S. MooseX::Declare absolutely did not compile on Windows last time I tried back in August. When I asked about this on IRC, it was a "known issue."
"private" is one of the worst programming language concepts ever invented. Perl's lack of this bug is a feature. Private methods are nice for documentation purposes, but most developers misuse "protected" and "private", and it makes their libraries nearly impossible to use. In a perfect world; great idea. In the real world; a huge waste of time.
For example, in my last company I wrote this ORM in Moose. I released it. And the guys I was working with just didn't get the concept of Moose. They kept accessing the attributes from the underlying blessed hash like they did before rather than the accessors. It still worked because internally Moose stores stuff the way Conway recommended in OOP Perl (which was a weird decision imho). One day I renamed one of my database columns and so had to rename my accessor and created a manual function that warned of "obselete call to accessor x" whilst returning the new column name... backwards compatibility, yay! But they didn't see this because they didn't use the accessor and now all their manual hash accessing was returning undefined or null or whatever it is.
Of course it only took me going over to their table and explaining to them how Moose worked or getting together for a meeting. Fine. But that was a company with a technical team of around 10 people. Now I work for a big tech company, and I'm in charge of a framework again except now its JavaScript which has the same problems when people force this classical inheritance pattern and now when some guy doesn't get it he's in another building or another country and did so in a repository I didn't even know existed and his code that breaks my encapsulation is in a production device and when I refactor my code I have the possibility that I break live code on some project or some device I don't know about.
So yeah, privacy is an important concept and if you don't think so I would say it is you who lives in a superficial bubble where everyone who uses your API or works on your code can be trusted to have good sense.
In an ideal world, you could rewrite every library that was designed incorrectly, but there is not always time for this. Intentionally limiting flexibility ("private") is worse for code reuse than allowing someone to intentionally change internal details. (Which is what privacy prevents.)
A good programmer will use his judgment to determine whether to cut-n-paste, subclass, or rewrite. The programming language should not remove options from his toolbox.
Now I work for a big tech company, and I'm in charge of a framework again except now its JavaScript which has the same problems when people force this classical inheritance pattern and now when some guy doesn't get it he's in another building or another country and did so in a repository I didn't even know existed and his code that breaks my encapsulation is in a production device and when I refactor my code I have the possibility that I break live code on some project or some device I don't know about.
Well, the tests stop passing. The tests are what ensure your code works, not "private" directives. "private" is just documentation that suggests "you probably don't want to call this directly". "Probably."
All I can say is that technical solutions to social problems never work. Rewrite your app in Haskell and I guarantee that dumbass developers will still be able to mess up your code. People are much more clever than programming languages.
Certainly not a waste of time. In comparison, a looser language like javascript where any code can call any code any time it likes, can be a complete pain to debug.
>> "It can call any code any time it likes, but it shouldn't"
Sure but it's nice to have a lock on the door that says "I can say with 100% certainty that this code will not be called from anywhere else". That's a pretty useful feature to have.
jrockway is a Perl programmer; he writes in the language he knows.
And if you object to comparing against "out-of-the-box" Java code, that's fine. My understanding is that there are, in fact, JVM-based tools that let you write code in the approximate style of Moose. The only stumbling block is that these tools are called "JRuby" or "Clojure", and the average Java programmer doesn't know or recognize them. [1] Certainly they make no appearance at all in the original article.
---
[1] Or maybe my impression is wrong and one or another of the alternative JVM languages really is on track for widespread recognition. I wouldn't know.
I read those exactly the same. There's no difference to me apart from taste, and potential for errors.
The potential for errors however, is far higher with the Moose and CLOS versions. Which is why I wouldn't like to use either of those syntax in a large project.
The java version, you can only really typo on the variable name. And even if you do that it likely won't compile unless your variables are similarly named. But with the moose example, you're starting to have silly things like '=>' which can easily be typoed and which can still compile, but completely change the meaning of the code.
Obviously at the extreme of this is regexp. Hugely compact, but a complete bitch if you typo anything. Personally I'd hate to write complete programs in regexp type syntax.
So you can keep your non-verbosity at the expense of sanity.
>> "In conclusion, Java is verbose, and verbosity is harmful."
I'd say Java has longer tokens than some other languages. By my definition that doesn't make it any more verbose. It makes it harder to go wrong with typos etc.
If you look at the underlying programming constructs in your first 3 examples, they're the same... thus for me, those code fragments are the same. No more or less verbose.
I think some of it just comes from having experience of reading code. Once you learn to read code in terms of programming concepts, rather than actual lines of code, you see no real difference.
In Perl, you say "has foo => ( init_arg => 'foo', isa => Whatever, reader => 'get_foo', writer => 'set_foo' )" and the correct code is generated. In Java, there is no concept of "attribute", so you have to build it every time by manually declaring the field, manually writing the constructor, and manually writing the getters and setters. Anything you do manually more than once has the potential to introduce a subtle bug, and more importantly, is tedious and degrading.
With due respect, surely that's what being a good programmer is about? There's no law against preprocessing java if you really dislike the defaults.
How many constructors do you write? Sounds like perhaps too many.
all languages suck for some value of 'task'.
So why not just cut-out the middle man and work in a higher level language directly?
Sure, that's the common misconception. You're correct. However, if you actually look at the language and write stuff in it, you'll find it's not actually true.
The issue is when people look at some existing insane Java code and cite it as proof Java is verbose.
I'd absolutely agree - most open source / freely available Java code is verbose. Several of the libraries (enterprise etc) are verbose and insane. But that's not Java.
Higher level languages are not always better. Use what is suited to the job at hand. I'm far more comfortable working at lower level than high level.
Which is better.... talking high level and relying on a compiler/runtime to figure out what you want? or just talking a bit lower level in the first place.
I don't know, that seems to be the very definition of "verbose" to me.
Just because some frameworks choose to use getters/setters, it doesn't make them any part whatsoever of the language.
Once again, people getting confused about what Java the language is.
If you have a "get_foo" method in your class, you can change what "foo" is under the hood without consumers knowing. If they touch the field directly, you can never change that -- your implementation becomes the interface. And the idea of OO (and good programming in general) is to avoid that.
You keep talking about "ignore Java's syntax and focus on being a better programmer", but I think if you use fields for your public interface, you are compromising the quality of your code. You should be able to have the protection that getters and setters afford without the cost of typing them into every class that needs them. That would allow the "better programmer" to write a better program.
It's not really rocket science to do a search+replace.
Getters+Setters have their place, where you may need to change things under the hood definitely. But most of the Java I see using them is just complete overuse of them for the sake of it.
FWIW, I only use getters+setters when I need to - eg they do something other than just get/set a variable. I think it makes for clean concise readable code.
It's not efficient to always use setters/getters, even if the language provides them.
The only sorts of things I find painful in Java is writing a class to do Comparator etc, but the number of times you need to do that can be counted on one hand.
Some people who write Java astonish me with their churning out of toString, equals, hashcode, getters, setters. Checkout a few open source projects and you'll see a whole set of files defining interfaces, then a whole mirror set of source files defining the implementation of that. Which is just ridiculous when there is only 1 implementation of everything. Makes the mind boggle, but often it's an IDE churning out autogenerated code (Does that still count as Java? ;) )
The problem with Java is that the interfaces are usually useless. Imagine you have a build backend talking to a stoplight (red/green widget). The logical interface is for the builder to emit success or failure, and for the stoplight to internally change that to red or green. But of course, many people decide that the build backend should emit red or green, and that the stoplight widget should display that color. So while the build backend isn't coupled to an implementation in theory ("it just uses the RedOrGreenShowable interface!"), it actually is.
Programmers....
2. Using getters for properties is "idiomatic" Java, or at least it was last I used Java a few years ago...
3. More importantly, here we're comparing equivalent code in different languages and the lisp code specified a reader so I think it's only fair that the Java version should have the equivalent getter.
You don't. You can use an inner class. An anonymous one, if you like. It's more verbose than your typical lambda expression but it's not 'a whole new file' by any stretch.
No you don't have to. Simply create an anonymous class deriving from your sort functor interface. With anonymous functions it would still be shorter, I have to admit. But it is less cumbersome than the way you suggest.
ResponseType myResponseType = responseTypeFactory.CreateNewDisagreementResponseType();
responseManger.SetResponseType(myResponse, myResponseType);
return myResponse;
But it's also a matter of style. I just wrote a function the other day to do the equivalent of:
Map m = makeMap( "key1", "value1",
"key2", "value2",
"key3", "value3" );
It's perfectly ok to write something like this in java using varargs, but because of ... I don't really know, habituation I guess, it took me learning Clojure to actually think of writing it.The author's argument seems to be that Java the language cannot possibly be criticised for being verbose as the verboseness of the language clearly pales in comparison to its outrageously verbose APIs. Not a good line of defense in my view.
I simply don't get some of its design choices ... operator overloading is not harmful if implemented right, checked exceptions were a bad idea, and why can't it have local type inference or proper closures and why can't developers define their own stack-allocated primitives? Where these really that hard or dangerous to blend in?
A lot of the time and effort that goes into programming is used to make code more compact and readable. That's hard to accomplish in java.
And by verbosity I mean real verbosity. Not the ill advised measure of 'lines of code' or 'tokens'.
A good programmer can write extremely concise well written Java.
Don't waste too much time on this sort of fluff, or chasing the next 'hot' language. Spend it learning how to become a better programmer.
I'd say the main reason Java is wrongly associated with verbosity is two things - It's widely used in corporate environments, where people are taught to do things inefficiently and in the most number of lines possible, and secondly, the use of IDEs, leading to lots of verbose automatically generated crappy code.
Don't waste too much time on this sort of fluff, or chasing the next 'hot' language. Spend it learning how to become a better programmer.
Bad languages get in the way of becoming a better programmer -- they make you program the language rather than program your program. I contend that Java is one of these bad languages. It is too low-level to really let you focus on programming.
I don't understand how people get this mindset. Languages are a moot point. They're all basically the same give or take a few matters of taste (And as I mentioned the probability of typoing and not being caught by compiler/runtime).
>> "It is too low-level to really let you focus on programming."
Sad you feel that there is no place for low-level programming, I personally find it the most rewarding and enjoyable. I certainly come from the Assembly world though, so perhaps why I don't see much wrong with Java. But you can't say enough times "Choose the tool that best fits the task at hand".
>> "I would like to read some of this "concise" Java."
Can't really help you there, I haven't seen much well written Java out there in open source projects etc. A lot of it absolutely stinks.
Just one data point though, The webserver I use for Mibbit (http,https,async,comet,websocket,sessions,etc) comes in at 6289 LOC. Functionally it's quite a bit ahead of tornado (5803 LOC). So concise Java certainly exists and can be written by competent programmers.
programmers make good code. Not languages.
Would shakespeare be worse had he written it in French? Do we spend ages moaning about how needlessly verbose it is in English?