4 ms·
As a simple example of this problem: In java7, the concept of 'I want to typecheck this expression, and if it is of the type I want, I want to do things to it
by rzwitserloot 6y ago
As a simple example of this problem:
In java7, the concept of 'I want to typecheck this expression, and if it is of the type I want, I want to do things to it based on that fact' was unwieldy in java:
-- JAVA 7 CHECK-AND-USE --
if (x instanceof String) {
String y = (String) x;
y.toLowerCase();
}
kotlin decided to address that:
-- KOTLIN CHECK-AND-USE --
if (x is String) {
x.toLowerCase()
}
within the 'if', x 'updates its type' automatically. Note that if java were to copy this, method dispatch depends on the compile-time type of the arguments, so if java were to apply this behaviour to its instanceof operator, that would break backwards compatibility (existing java code would mean something else just because you updated java versions). The obvious thing happened: Oracle / the JCP rejected this outright. Hence, it doesn't work that way in java and never will.
But this problem WAS tackled by java, and in a much different way: Java has generalized this concept as pattern matching and went all in on that.
-- ACTUAL FUTURE-JAVA --
// preview feature in 14, will be in 15.
if (x instanceof String y) {
y.toLowerCase();
}
// -OR--
if (!(x instanceof String y)) throw new IllegalArgumentException();
y.toLowerCase();
and, taking pattern matching to the next level:
// will likely be preview in 15 or 16.
// Given:
value class Point {int x, y; }
if (p instanceof Point(x, y)) {
// you can use x and y (both int) here
}
Which goes much further than kotlin and is quite different. So now kotlin has 3 options and they are all bad:
1. Also adopt this syntax. But now there are 2 different ways to do the same thing, so kotlin is now the language that feels like a jumbled mess that needs cleaning up, with pointless style debates and an increased learning curve for no good reason.
2. Ditch their existing syntax and adopt this syntax. That means kotlin breaks their own backwards compatibility, and everybody needs to either accept they can never update their kotlin, or they need to find all spots in their codebase that uses the old construct and update it.
3. Don't adopt this, and be in the situation that java has _more_ language features that kotlin. Or adopt pattern matching differently, at which point the idea that it's 'easy' to switch from java to kotlin starts taking a hit... and likely kotlin's design will start to feel jumbled because features are spliced on ad-hoc and mostly informed by dueling concerns.
- ImprobableTruth 6y agoWhy wouldn't they just extend their existing syntax? i.e. something like if (p is Point(x,y)){ //p : Point, x: int, y: int } or hell, adopt any other pattern matching syntax. "Either they adopt the java syntax or are missing features" seems like a false dilemma to me.
- rzwitserloot 6y agoIf they adopt other syntax, then kotlin is no longer 'java, with the warts papered over'.
- ImprobableTruth 6y agoBecause they'd be using 'is' instead of 'instanceof'? That just really doesn't strike me as a meaningful difference. Hell, they already have a lot of way more crazy changes (the radically different lambda syntax, different function signature, functions outside classes, extension functions, etc). By your reasoning they're already way past the point where "it's 'easy' to switch from java to kotlin".
- stickfigure 6y agoThere are already plenty of differences between Kotlin and Java, this would be a minor one. I work reasonably comfortably in Java (largely due to lombok - thanks!) but keep an eye on Kotlin and... well, I much prefer the Kotlin syntax here. I'm sure in the 5% case changing variable names is useful, but in the 95% case it's just noise.
- half-kh-hacker 6y agoWhat's wrong with: data class Point(val x: Int, val y: Int) // Later on... if (p is Point) { let (x, y) = p // x and y are of type Int } That Kotlin has, already, now?
- apta 6y agoIt doesn't allow you to match on individual members of the class. For example, in future Java I should be able to do: if (p instanceof Point(0, 0)) { // handle origin } else if (p instance of Point(x, y)) { // ... } and it will get even more complex with matching on nested types (e.g. `if (r instance of Rectangle(Point(0, 0), Point(1, 1))) { ... }). This will be much nicer once it starts working with `switch` expressions too.
- donmcronald 6y agoIt's been a while since I've used Java and it seems weird to me to see instanceof used to check values instead of types. That sounds a lot like instance of being used instead of .equals(). Why not? if (r.equals(new Rectangle(Point(0, 0), Point(1, 1)) s) { s.doStuff(); } Are they really planning to use instanceof to check for equality?
- apta 6y agoIt's not exactly the same as equality checking, as each has its own uses. You can override `equals` to reflect specific semantics. Say for example you have a non-public ID field for your Point class that is used to determine uniqueness, such that points with different IDs are not equal regardless of their x, y values (maybe not the best example, but the point holds, arguably however, that would be bad practice for records anyway). Secondly, using equals as per the code you provided incurs at least 3 allocations (1 Rectangle and 2 Points), whereas using pattern matching there are no such allocations. Furthermore, and probably the main point of pattern matching, is that it allows for "deconstructing" records into variables so that they can be examined individually. I can handle any Point on the x-axis at y = 0 by simply matching against `p instanceof Point(var x, 0)`, or all rectangles with their first point is the origin by matching against `r instanceof Rectangle(Point(0, 0), var p)`, it's much cleaner and clearer than writing something like: if (r.getFirstPoint().x() == 0 && r.getFirstPoint().y() == 0) { var p = r.getSecondPoint(); // ... } Not to mention once we have sealed types we can do something like: var area = switch(shape) { case Circle(var r) -> Math.PI * r * r; case Square(var s) -> s * s; case Rectangle(var l, var w) -> l * w; ... } Pattern matching has been available in many other languages (e.g. the ML family of languages, and some JVM languages like Scala), and now Java is also gaining this feature.