Live data from Hacker News

Never Use toString() for Behaviour

java.christmas

41–50 of 51 posts

Re: Never Use toString() for Behaviour

#41

> If you used the returned Country to construct an API request including the name of the country, this will now produce strange countries named as “Optional[“Norway”]” I am not too familiar with java, but should’t you call get() from optional to get Country from Optional and toString of Country will behave as expected.

As the sibling comment says, the post's advice is really about making your code refactoring-friendly with regard to type checking. You generally would not want to call '.get()' without an 'isPresent()' check because it will throw a NoSuchElementException if the Optional is empty. This is a foreseeable case, so most would like to handle it somewhat explicitly. Today, it is idiomatic to use '.map' to transform it: Opti…

orElseThrow does return a String, not an Optional. You also could use orElse(null) or orElse("null") depending on the use case (eg. using as a part of toString).

Re: Never Use toString() for Behaviour

#42
post #39

Earlier quoted context omitted.

Yeah, it really would be helpful if the method name made that clear. The language documentation does make it clear what toString() is for. And it's convenient to be able to (for example) stick objects and strings together with the "+" operator and have conversions automatically happen. But (understandably) not everyone reads the language documentation cover to cover before they start writing Java code, so you can't c…

java.lang.Object has a few methods though. At least those should be known when one starts writing productive code. Otherwise one produces even worse bugs than relying on toString behavior (equals and hashCode, god forbid). Cover to cover would mean understanding the memory model and maybe garbage collection. That's too far certainly but a few basics would do no harm. toString is surely one of them.

I'd hazard a guess that 0.1% of Java developers are aware that on some operating systems Object.wait() can awaken spuriously. Heck, it wasn't even in the Javadoc until 1.5. And don't even get me started on how you should handle an InterruptedException.

Most Java developers I know consider rolling your own multi-threading scheme about as wise as rolling your own cryptography, and just pretend notify/notifyAll/wait don't even exist.

Re: Never Use toString() for Behaviour

#43
post #2

Agree in general, but note that Java itself does not follow this rule: StringBuilder's and StringWriter's toString methods use toString for behavior — creating a string from accumulated chars, and I'm fine with that.

This also goes for logging and even string-ification in a lot of popular Java libraries. It's certainly good to write methods like getName() if you foresee an issue with changes down the line, but precisely because toString is universal, it may well be invoked somewhere outside what you're writing.

Re: Never Use toString() for Behaviour

#44
post #39

Earlier quoted context omitted.

java.lang.Object has a few methods though. At least those should be known when one starts writing productive code. Otherwise one produces even worse bugs than relying on toString behavior (equals and hashCode, god forbid). Cover to cover would mean understanding the memory model and maybe garbage collection. That's too far certainly but a few basics would do no harm. toString is surely one of them.

I'd hazard a guess that 0.1% of Java developers are aware that on some operating systems Object.wait() can awaken spuriously. Heck, it wasn't even in the Javadoc until 1.5. And don't even get me started on how you should handle an InterruptedException. Most Java developers I know consider rolling your own multi-threading scheme about as wise as rolling your own cryptography, and just pretend notify/notifyAll/wait don…

Ironically, that's one of the few things I do know off the top of my head about Object.wait(). I'm far from an expert on Object's threading methods, but "this will burn you, don't try it" is becoming more pervasive than "here's how to do this". Which is almost certainly the right order to learn those things in.

Re: Never Use toString() for Behaviour

#45
post #18

Earlier quoted context omitted.

Why so, what are the downsides? It's not like it introduces any meaningful overhead at all, and there are plenty of situations where it is useful even in production (eg for logging in catchall "can't happen" top level exception handlers). Adding yet another compiler flag seems like a worse solution to me.

What you lose is the ability to model objects based on the behaviours that they can perform. I want to be able to design objects that have toString() and I want to be able to design objects that don't have toString(). The same goes for clone(), hash(), getPtr(), getEndian(), equals(), toBytes(), toJson(), encrypt(), delete(), isNumeric(), toXml(), serializationVersion() or compare().

I suppose you could define toString() to throw an error, but obviously that's a messy hack in place of having that control. I can definitely appreciate the logic for making nothing intrinsic, and I can also see an argument for making equals() the only method of Object. (i.e. everything should have an identity function, but nothing more.)

Now I'm wondering... are there languages which formalize "implement with a throw" into some kind of explicit refusal to implement a method? Obviously there are method-sharing approaches other than inheritance, but I've never heard of "you must implement this, or explicitly choose not to".

Re: Never Use toString() for Behaviour

#46
post #41

Earlier quoted context omitted.

As the sibling comment says, the post's advice is really about making your code refactoring-friendly with regard to type checking. You generally would not want to call '.get()' without an 'isPresent()' check because it will throw a NoSuchElementException if the Optional is empty. This is a foreseeable case, so most would like to handle it somewhat explicitly. Today, it is idiomatic to use '.map' to transform it: Opti…

orElseThrow does return a String, not an Optional. You also could use orElse(null) or orElse("null") depending on the use case (eg. using as a part of toString).

I'm useless without a compiler to check my types!

Re: Never Use toString() for Behaviour

#47
post #18

Earlier quoted context omitted.

What you lose is the ability to model objects based on the behaviours that they can perform. I want to be able to design objects that have toString() and I want to be able to design objects that don't have toString(). The same goes for clone(), hash(), getPtr(), getEndian(), equals(), toBytes(), toJson(), encrypt(), delete(), isNumeric(), toXml(), serializationVersion() or compare().

I suppose you could define toString() to throw an error, but obviously that's a messy hack in place of having that control. I can definitely appreciate the logic for making nothing intrinsic, and I can also see an argument for making equals() the only method of Object. (i.e. everything should have an identity function, but nothing more.) Now I'm wondering... are there languages which formalize "implement with a throw…

I'm not quite sure what you mean, but it's standard in python if you have a method in a class that must be subclassed and overridden you can have the default raise a NotImplementedError

Re: Never Use toString() for Behaviour

#48
post #39

Earlier quoted context omitted.

Yeah, it really would be helpful if the method name made that clear. The language documentation does make it clear what toString() is for. And it's convenient to be able to (for example) stick objects and strings together with the "+" operator and have conversions automatically happen. But (understandably) not everyone reads the language documentation cover to cover before they start writing Java code, so you can't c…

java.lang.Object has a few methods though. At least those should be known when one starts writing productive code. Otherwise one produces even worse bugs than relying on toString behavior (equals and hashCode, god forbid). Cover to cover would mean understanding the memory model and maybe garbage collection. That's too far certainly but a few basics would do no harm. toString is surely one of them.

Most Java developers don't know what to do when something can throw InterruptedException. I'd argue that bugs caused by broken code that catches this exception are much worse than what can happen when not using toString() correctly.

Re: Never Use toString() for Behaviour

#49
post #5

>If left unoverridden, it only yields a description of it’s class and location in memory It contains the hashCode(), not the memory location. The default implementation of hashCode could use a memory location to generate the hash, however that isn't the case in all implementations. OpenJDK seems to have used an RNG by default in the past and currently derives a value from the thread state[1] [1] https://srvaroa.githu…

Thanks for the reply - the author (who doesn't have a user here) agreed completely, and the article has been changed to reflect that.

Re: Never Use toString() for Behaviour

#50

Earlier quoted context omitted.

How does Lombok help to avoid this?

Lomboks annotation @ToString will automatically generate the typical overridden toString() method based on the fields.

I just tried this in Java and I ran quickly into circularity issues (one of the fields has an indirect reference to the same object, say a list which the current instance is part of). How does Lombok cope with that?

It's not that easy as I thought (see my sibling comment).

Post reply on HN