Hacker News new | past | comments | ask | show | jobs | submit login
Common I/O Tasks in Modern Java (dev.java)
130 points by infodaemon 47 days ago | hide | past | favorite | 23 comments



Note that

    try (Stream<String> lines = Files.lines(path)) {
        ...
    }
has a catch (pun not intended ;)), in that if you write

    catch (IOException ex) {
        ... handle it ...
    }
this surprisingly only catches I/O errors thrown directly by the lines() method, but not those thrown during the stream processing, since those are UncheckedIOExceptions. This is because the Stream API designers decided (very unfortunately, IMO) to not want to deal with checked exceptions.

I therefore prefer using Files::newBufferedReader and BufferedReader::readLine.

———

The `new Scanner(path)` example is arguably even worse, because upon I/O error the tokens() implementation simply behaves as if the end of the file was reached, and you have to manually check with Scanner::ioException if an error occurred.


The "URL.openConnection()" API has always looked out of place to me. I thought it was deprecated at some point but guess not. It feels backwards. Like you would not do "select * from tbl".execute(db)..

There was something more fucked up about the URL API which I can not remember. I think it triggers blocking DNS resolution on construction or something. Haven't written Java for more than 10 years though. Might be mixing something up.


When you do equality checks on two URL instances, it checks to see if they resolve to the same IP address by hitting up DNS. This is problematic because equality checks happen implicitly all the time, e.g. if you're putting URLs into a hashtable.

https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#...


No shot this is _ever_ what is actually meant by ‘if urlA == urlB’


Well, remember, this is Java, so `if (urlA == urlB)` just does standard object equality, the IP lookup is only done for `if (urlA.equals(urlB))` :)


This is Java, so you should always do `Object.equals(urlA, urlB)`, on the off-chance urlA is null!

(I do not miss working in Java haha)


It's still insane behavior. This implementation is effectively a sharp edge in the dark. Have fun.. :-/


Yes. Use the newer URI class instead if you want a value object without “smarts”. URL instances also contain a URLStreamHandler, which means that two URL instances that compare equal can still have different URLStreamHandlers that handle the URL differently. Instances of the class URL are really service objects.


You're probably thinking of .equals for URL, which does DNS resolution. The equality can therefore change depending on your network connection.


Not sure what's worse: the potentially changing equality, that you can get blocking i/o on something as supposedly local as using URL as keys in a hashmap, or the fact that identical paths on different hostnames that happen to resolve to the same IP are decidedly not the same resource ever since http 1.1 introduced the Host header.

It's a shame that Java missed the opportunity to duplicate all functionality expect the absurd equality on URI (including weirdos like openStream), so that the old URL could properly focus on celebrating xkcd:1172:workflow without doing too much harm.

What's the sin of File by the way, besides not being quite as convenient when interacting with modern API? Unfortunately the article is very silent about why it needed to be substituted instead of extended. I smell a similar story as URL vs URI, just not quite as bad?


File is from java.io, while Path is from java.nio (New I/O), which is a complete rethink of how I/O works, going from a "steam of bytes" paradigm to one of buffers and channels, enabling non-blocking I/O and DMA transfers.

In particular, File conflates several concepts: a filesystem, a path to a file in that filesystem, and a file's contents as byte streams. Path only represents a path, you need to use a FileSystem object to read from a file at that path, and you get a Channel for reading/writing that is used to fill a Buffer.

However, File does not need to be substituted. java.io is not deprecated nor is it slated for deprecation, and there are easy ways to convert from File to Path and vice-versa.


But nothing in File is tied to "stream of bytes", it's really just an immutable ID that may or may not point at an entity in a file system. What it adds is operating on filesystem attributs in a best effort WORA way, which leaves room for improvement (nio allows decidedly non-WORA digging into details I think?), but is very valuable to have because it satisfies most use cases in a more convenient way.

As you said, File won't go away. But it really does not feel right to not have File implement Path, or perhaps a subset of Path that would be good enough for all those use cases where something outside the identifier acts on the identified entity. Every .toFile() and .toPath() is a little defeat in API evolution, and there are a lot of those. Do they consider addition of an new interface, to an existing class a break of compatibility? On 17 windows (that's what I happen to be looking at), a chain of toPath/toFile/toPath won't even give you a symmetrically bound pair of immutables, toPath is memoized but toFile is not. Should not have been too hard keeping a provenience reference on the other side, or have memoization on both sides?


That seems insane to me—two identical urls can trivially resolve to distinct IP addresses. Not to mention doing IO on what looks to be a simple computation.


It's been a while for me too, but I think I recall common advice to use java.net.URI instead of java.net.URL wherever possible, at least partly for that reason. Also, the javadocs of URL now state: "The java.net.URL constructors are deprecated. Developers are encouraged to use java.net.URI to parse or construct a URL."


These articles are useful for grognards like me who have these patterns etched into muscle memory from 20 years ago.


i like all of these except passing a remote url to .readAllBytes(). this seems fine for coding little things on your own. but in production i would recommend instead setting up an http client with the settings you want, calling a get merit that returns an input stream, and then calling .readAllBytes()


Java I/O has come a long way since the Java 7 days, not to mention the rest of the language. I remember being stuck on Java 7 until 2019 and lamenting the nice features we couldn't use.

Rust I/O feels like it was inspired heavily by Java's I/O and it owes a lot of credit.


Too bad there still isn't a way to delete recursively a folder without manually traversing it.


Yeah, do not forget to close streams always, when you do not know, where the stream come from.

FileSystem from Zip, i did know that. Does it also work for writing?

And people use var nowadays. Pity, val has not found its way into java.

And i wish for more useful stream operators, which can lift streams up, like groups. And tee is finally in it, but looks a bit bloated when being used.

And parallelStreams(), people always forget, that it is not good for long running tasks. You always block the default executor.

But in general, iam happy with the progress. We have pressure from Kotlin, but so far, i have not felt the desire to switch.


One can “final var” in Java. I thinks it’s enough and less confusing than a single letter difference between two keywords.


Of course. But it feels superfluous. I have written in scala for a while and val stuck with me. Well, i still have lombok.


Kotlin is far from perfect, but it’s a much better language than Java. Just the nullable types alone make it worth using.


Var is somewhat controversial and shouldn’t be used indiscriminately anyway. Chromium’s Java styleguide is a good example: https://chromium.googlesource.com/chromium/src/+/main/styleg...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: