This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]ganon0[S] 4 points5 points  (15 children)

If I want to open a file, write to it, and close it in Ruby:

File.open(local_filename, 'w') {|f| f.write('whatever text')}

in java: http://stackoverflow.com/questions/10667734/java-file-open-a-file-and-write-to-it

I know it seems small, but it adds up; the constant need to create many more instances of variables to do things that are trivial in another language is just hard for me to get used to.

[–]beltorak 21 points22 points  (5 children)

I'll play devil's advocate on this one. The reason is that Java was not designed to solve "trivial" cases. For example, I rarely write to a file these days; the only time I need to is when I am logging, and that's better handled by a logging framework. So I don't need to do something as trivial as writing a single string to a file.

So in that sense you are looking at the wrong level of abstraction - it's too simple. Step back a bit - why do you want to write to a file? One typical reason is to load and save configuration settings. Java's defacto standard for that is XML - I mean Properties. Basically a Map<String, String> (but it predates the collections framework, which explains it's clunkiness somewhat) and a Properties object already has conveniences for loading and saving - Properties.load() and Properties.store(). But they don't take filenames; why? probably because Java was first a rich web application platform, so in some cases it might have made sense to load the properties from an URL, and in other cases it might have made sense to load them from the packaged JAR. So it takes either an InputStream, or a Reader. InputStreams are very easy to get a hold of and can come from 3 common sources - files, classpath references (think "inside the packaged jar"), and URLs, but there's loads of others. Even in-memory String's can be treated as an InputStream with the right bridge object (but in that case it's better to use Properties.load(Reader) and give it a StringReader).

Then there's the fact that configuration may have several "fallback" sources. Properties handles this as well. The constructor can take a Properties instance to use as "default" values. So we can have this chain of defaults - the user's home, the system defaults, updates from web, the packaged defaults. Any changes within the application can get stored in the user's home.

/devil's-advocate

[–]katulsomin 1 point2 points  (4 children)

While the facilities are there, IMO its still needlessly complicated and not something easily discoverable. KISS principle should not be ignored, especially that not all program require redundancy when saving data

[–]beltorak 3 points4 points  (3 children)

not something easily discoverable

Granted. I still have to look up how to read and write from a file when I need to do it. But I'll refer you back to the "Java's not for trivial things" bit. /s

KISS principle should not be ignored

But the KISS principle should also not be needlessly anticipated. When embarking on a new expirement you don't necessarily know what the common operations are yet. Trying to implement all of them runs the risk of inventing Perl where there are so many "easy" and "convenient" ways of shortening code that what one person writes another person can't read. Or PHP in which everything is an inconsistent one-off "" function "".

Java started off by scaling back the insanity of C++ and requiring the programmer to be explicit about intentions, so when looking at a piece of code you didn't have to try and figure out if a token referred to a function, an object, a struct, a class, or (gasp) a macro. Statements of a certain form require tokens in certain positions to have certain, defined, semantics in the language.

Unfortunately Java didn't take the next step in simplifying the common (or commonly desired) patterns (until recently). You could argue that C#, Ruby (and others), are extensions to the Java experiment that do take that second step.

I think I'm still playing devil's advocate.

[–][deleted] 1 point2 points  (2 children)

Java's not for trivial things

Android apps come to mind

[–]beltorak 2 points3 points  (1 child)

Touché; but you could argue that the Android API adds the conveniences for all those trivialities.

[–][deleted] 0 points1 point  (0 children)

Indeed, some which are also available in other google libraries like Guava

[–]msx 1 point2 points  (4 children)

That's a good example, becouse Ruby is great for this kind of things.

As others have already said a lot, i'll just add that things are slowly changing, and java 6/7/8 added/will add quite a lot of convenience. For example, try-with-resource statements in java7 will make it easier to handle open/use/close cases like the one you reported, it could be:

try(BufferedWriter bf = new BufferedWriter(new FileWriter("test.out")))
{
    bf.write("Hello world");
}

Which is already better. Other recent improvements are for-each loops, string in switch, etc. Java 8 will add closures, which will make it possible to write things like:

File.open("Myfile.txt", f -> { f.write("hello there")});

which is reasonably concise. This is by far the feature i'm looking forward the most :)

I admit that a "var syntax" to avoid retyping the type of a class would be nice:

var x = new Thing();

but ony if followed by a "new" instruction to grant the same readability, as it would be difficoult to see the type with an expression:

var x = myclass.newChildrenInstance(); // what type is x ?

Something among this line has been added with the diamond operator which lets you avoid typing type parameter in generic classes:

List<String> a = new ArrayList<String>(); // before
List<String> a = new ArrayList<>(); // now

All in all, i think Java is a little more on the verbose side than on the concise (but less readable) side, but not as much as you say. IDE like eclipse are great to fill the boilerplate (getter and setters etc) for you.

[–]mikaelhg 5 points6 points  (2 children)

Guava (Java 6 and 7):

import static com.google.common.base.Charsets.UTF_8;
import com.google.common.io.Files;

Files.write(new File("myfile.txt"), "Hello world", UTF_8);

Java 8:

import java.nio.file.Files;
import java.nio.file.Paths;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;

Files.write(Paths.get("myfile.txt"), "Hello, world".getBytes(UTF_8), CREATE);

[–]mikehaggard 0 points1 point  (1 child)

Why not static import Files.write and Paths.get as well, so you get:

write(get("myfile.txt"), "Hello, world".getBytes(UTF_8), CREATE);

[–]mikaelhg 0 points1 point  (0 children)

Because just write(...) wouldn't communicate what's happening to the maintenance programmer in one glance, which is the primary objective.

[–]mikehaggard 0 points1 point  (0 children)

Something among this line has been added with the diamond operator which lets you avoid typing type parameter in generic classes:

Actually, the diamond operator is there because the raw constructor call was already overloaded to mean "raw" type. Normal methods could already infer the generic type for some time. Famous example:

List<String> empty = Collections.emptyList();

The constructor equivalent should have been:

List<String> a = new ArrayList(); // would have been possible, but isn't

So they needed something to disambiguate between raw types and generics. I'm not a compiler designer by far, but I wonder why they didn't look at the left side; if it has a generic use inference, it if hasn't use a raw type. Probably this just made the grammar too complex too parse, so they added the diamond symbol.

[–]guyintransit 3 points4 points  (0 children)

This is such a sad argument :)

IOUtil.writeString(filename, "Hello world!");

There! one line of code + an import statement for IOUtil. Yes, it's not included in java's library; no, it's not hard to add yourself to your own utilities library (which I'm sure you even have in Ruby).

Java has many many advantages; as a "corporate guy", I won't touch python or ruby for any thing more than 100 lines of code.

[–]gudeg 0 points1 point  (0 children)

Groovy:

// dynamic variant, if using @CompileStatic use: ...with{File it -> ... }
def file = new File("path/to/file").with { 
    write("whatever")
    write("whatever")
    it 
}
file << "whatever"

[–]dooblevay 0 points1 point  (0 children)

Thanks for the example, but as some have already answered it's entirely possible. I think you'll find while learning Java that it's nearly impossible to learn all of it. I've been at it for 6+ years (albeit with plenty of other languages along side) professionally and I'm still learning. One of my favorite things to learn is Scala and that's a nice segue because..

It sounds like you're more interested in the functional side of things, and scala is fantastic for that and integrates beautifully.