Java 14 is getting an experimental preview of Records (https://openjdk.java.net/jeps/359) which takes care of much (but not all) of ceremony around "data classes".
Things are coming. That said, I hardly believe those minor syntax improvements are what is so "good" about Kotlin. It has some better defaults (non-null by default, final classes by default), and is a more modern language. Java will stay with us for many years to come, though.
I wonder if Java's speeding up of new language features over the past several years, after many years of a stagnant feature set, is a direct result of pressure from the likes of kotlin.
It is mostly C# that is keeping Java on their toes. Java competes with C# than any other language in this world. In the enterprise space, it is either Java or C#. If you check most of their improvements, they were done ion C# first, then they follow.
Now that is a good strategy. Copy directly from a language that is moving at a very fast pace and competing directly with the language you want to replace.
Who has it now is a good question, but so too is 'who had it first'.
And if you also consider the upgrade treadmill, and where the average Java developer is relative to say ten years ago, it may turn out that at any given point in time, the Kotlin version you'd be able to deploy may have a number of features the most recent Java version you could deploy does not have.
(to say nothing of companies where 'the version' is decided by committee or fiat and thus having a more obscure language sometimes gets you less scrutiny).
It's not so much pressure as data. Java is conservative by design. Even back in 1997, James Gosling wrote that Java only adopts features after they've been tried in other, more adventurous languages, and then Java picks those who've shown the best cost/benefit tradeoff, and only after enough people seem comfortable with them. Java, then, depends on languages like Kotlin or Scala for crucial data about features. The interesting thing, though, is not which features Java adopts, but which ones it doesn't.
> You are right, but you still have to catch the checked
> IOException.
Rather than which alternative? There are tonnes of IO errors that can occur and are captured in Java that you may not ever even consider. IO is exceptionally tough and programmers should be aware that there could be 1 of a possible million reasons for failure, even if they don't care exactly why.
It's not a massive ask either:
try{
/* Perform some IO that 99.999999% of the time */
}catch(Exception e){
/* Something went wrong and we don't know the state of the disk */
}
Personally I believe applications should have their own wrapper around IO and handle it accordingly. I.e. not caring, re-trying, show-stopper, etc, etc.
A lot of code I've written will have the following if I really don't care if it works or not:
try{
/* IO code */
}catch(Exception e){
/* Do nothing */ // <-- Let others know that this was on purpose
}
The problems are that (a) you need to add an extra type parameter for exceptions all over the place, (b) exception unification (sum type) doesn't work generically - you can say E1 | E2 in a catch block but it's not a real type, and (c) it composes poorly with existing libraries that expect Consumer<T> throughout.
For one level deep callbacks (for resource handling and the like) it works reasonably well though.
For a lot of scripts you want the program to crash with a stack trace if you get an IOException, which is the default behavior if it's uncaught. The user is the developer, and if there's an error the developer fixes it by munging something on their filesystem.
Java was designed for "production" software, which in the late 90s and early 00s meant software that ran for long periods of time as a server, servicing thousands of mission-critical customers. Crashing was not acceptable behavior for that. But now a lot of production software often requires a lot of ancillary one-off tasks that are just done by the developers - testing, data-munging, exploratory code, migrations, demos, etc. That's a very different environment from where you code to spec, the spec never changes, and once the software is done it's supposed to run for years without crashing.
Early Java was designed for set-top boxes and then web browsers. Large-scale servers came later; in fact a lot of libraries still don't support the async futures that were officially added five years ago.
I actually like it. I also create my own exceptions for rare (exceptional) cases, just not to forget to handle them.
But some Java SDK exceptions drive me crazy. For example `URL.parse("http://example.com")` over hard-coded strings that I know 100% won't throw exceptions, but I still to catch 3 of them.
After URI appeared in 1.4, the only reason to use URL was to create a URLConnection from a URI. Since openConnection() throws IOException, it's not a big deal that toURL() throws a MalformedURLException - just catch it along with all the other IOExceptions.
Since 11, there's no reason to use URL at all, because you can use HttpClient to actually do HTTP.
Ok I'm a veteran Java developer that understands exactly what you're saying but try explaining that to people unfamiliar with Java or junior engineers and watch their brains glaze over.
The JDK is filled with a ton of dumb "once upon a time we thought this was okay..." things that don't properly encode the correct modern idiom
For a long time I've wanted a sort of "local deprecation" tool where we could have a list of things in the JDK that shouldn't be used, and any direct reference to them would cause our build to fail.
Yes, it absolutely is! But then so are all other languages of its vintage. Part of learning a programming language is learning that.
Newer languages definitely have an advantage here, in that they haven't been around long enough for people to have figured out which bits of them are dumb.
It's worse. In 1.4 they added URI because URL is very flawed. You should never use URL.
The equals method actually does a DNS resolve and compares the ip addresses. This means that comparing two URL's on the same server will always return true when compared. A lot of URL's you compare will give you true as there are a lot of sites run on the same machine with the same ip address.
Had this happen in production in a third party library we used many moons ago. Sysop came and asked us why we did six figure DNS lookups over a short period of time (24h? Less? Don't remember).
Would probably have gone unoticed at most AWS/GCP/Azure shops today.
Worse was when I had somebody adding the ip address as an Inet4Address on every message passed between machines in a production environment that explicitly didn't have DNS (banks have occasionally very odd ideas about securing subnets). Every single message was doing a reverse DNS lookup and then timing out. And there were a _lot_ of messages.
Wait, seriously? Did whoever designed that have any idea what URLs actually were? Even without DNS, comparing the hosts is no way to compare URLs.
I can't think of a single example of two 'equal' URLs whose strings don't match (up until the parameters, at least)...
Yes - bear in mind the URL class has been around since 1995, when the landscape was somewhat different!
At that point, URLs resolving to the same IP were considered equal, even if the host names were different. Even now, there’s no real difference between say “http://example.xn--com-9o0a and “http://example.com:80”; it would have been reasonable to consider these equal.
Those are certainly not equivalent, as the 1st would not even resolve in the majority of environments. I'm assuming the 2nd host is a reverse proxy for the 1st? Unless that fact is part of your design assumptions (which it certainly can't be for a general URL class in a standard library), those should be considered different.
If the two hostnames resolve to the same IP address, they are equivalent. This could happen if initech.com was a default domain. This is a very standard situation in internal networks which i am surprised you are not familiar with.
I am quite familiar with that kind of setup and it's even further from equivalent than what I assumed you were referring to. Shared web hosting is the common example, but some other protocols are capable of distinguishing between hostnames.
google.com and maps.google.com resolve to the same address but are not equivalent. Even if your example and many others are equivalent, there are many that aren't. A general library should not make assumptions like that, unless they hold 100% of the time.
Looking back, I did phrase it ambiguously. What I meant was two URLs that are equal by definition, (no matter the server config, assuming standards compliance). A (general) library should make no more assumptions than the standard it implements. What I was asking about were cases, similar to http://h/p?x1=a&x2=b being equal to http://h/p?x2=b&x1=a since query parameter order is defined to not matter.
The spec I am mostly familiar with is, however, rather new, and whatever standard controlled URLs at the time (if any) might have made more assumptions.
Most languages with Either/Result have some form of monadic composition and type inference. This lets you basically ignore the error condition until you get to a level where it's appropriate to handle it, and the compiler will check the error types for consistency without you having to specify anything explicitly.
Either/Result has often been found to be untenable without these mechanisms, as well. Rust first added the try! macro and then the ? operator because it was so tedious to deal with raw Results otherwise.
This works for checked exceptions as well, just bubble it up to a place where you want to handle them by stating that your function might throw those exceptions.
I love both, but Java's implementation of checked exceptions cause harder and harder problems, especially when combined with tools such as lambdas, since there's no way to generically compose or handle checked exceptions inside them.
Proper Either/Result would fix some of the exception problems with lambdas in Java, though.
It also allows the exceptional behavior to be defined and to be controlled by the developer, one thing that you don't really have today when throwing RuntimeExceptions with say Stream APIs.
Java's checked exceptions are "proper Either/Result." The problem is that they can be of a more complex types (union types) combined with subtyping. In other words, what you see with Either is not a result of it being written as a return type, but a result of it being a much simpler type than Java's exceptions.
The problem boils down to the fact that you can have a disjunction of any number of checked exception types (including zero). No other party of the type system allows disjunctions, so it causes a lot of problems. The checked exception is conceptually part of the return type, but is split out.
I wish Kotlin had, instead of ignoring the existence of checked exceptions, instead translated them into part of the return type. I use Kotlin a lot these days, and one annoyance for me is dealing with code that throws exceptions. They fixed the annoying "(almost) anything can be null" problem of java and replaced it with an equivalent problem. Why can't nullability and failure results both be part of the static type?
(The workaround it to manually use an Either type yourself, but it doesn't help you with calling anyone else's code, since virtually everything throws exceptions on failure.)
Yes, but they are the only part of Java's type system that allows sum types.
How do you declare a method that generically takes a function that in turn takes a K, returns a V, and can throw whatever checked exceptions it wants, and you'll rethrow them? Last I checked, this wasn't possible in Java.
If checked exceptions were instead replaced with sum type return values, then it becomes trivial.
@FunctionalInterface
interface ThrowingFunction<T, R, E extends Exception> {
R apply(T argument) throws E;
}
static <T, R, E extends Exception> R apply(T argument, ThrowingFunction<T, R, E> function) throws E {
return function.apply(argument);
}
But not catch and rethrow it, because throw and catch aren't generic, and the type parameter isn't reified. You can emulate reified generics with the usual trick of passing a Class object:
Right, it's horrid, and it only works for exactly one checked exception type. So there's no way to define a single `Stream.map` (for example) that takes a function that throws whatever number of checked exceptions and just propagates them.
> Java's implementation of checked exceptions looks pretty minimal and sound to me.
>
> How would you implement them?
Java's checked exceptions aren't "minimal" and are problematic because they behave unlike the rest of the types system. I'm not saying that they should have ditched checked exceptions and left it at that, though. What they should have done was make sum types a first-class part of the type system, and then checked exceptions become redundant.
In languages that actually have general sum types rather than special-casing the way Java's exceptions do, you can get multiple types of errors with an Either by making a union of the different types of errors. You can alternatively use an N-way sum type as your return value in place of the 2-way Either.
Kotlin has sealed classes, which can be used to create a sum type, and you can even do the same in Java with enough boilerplate, but in either of those languages you run into another problem if you try this: it doesn't interoperate well with the vast majority of existing library code that throws exceptions.
It's certainly fair to say I have no alternative recommendation. I enjoyed using them prior to Java 8, and they gave the guarantees I was looking for. They have just had a much harder time integrating with newer features than could be hoped for. I'm not sure how much of that is intrinsic to checked exceptions, and how much is intrinsic to Java's implementation.
One example of this: there is no way to encode the type of a checked exception in a generic. I would like to be able to express something like the following:
But there is no way to express that 'throwing E' part. The type of a checked exception is firmly embedded in the interface. So I can't supply, say, an IO method as a Function<S, T> parameter, since the IOException causes a mismatch, and I'd have to write a handler specifically for methods that throw IOException, another for those that throw TimeoutException, a third for those that throw IOException AND TimeoutException, etc; a fairly fruitless goal without automatic code generation.
Interesting. I tried this years ago, and it never worked. Maybe that's changed in recent versions? Arity versions are still more feasible at least, and many languages are willing to pay the cost for them.
AFAICR this has always worked. But you rapidly run into limitations - as i say in another comment, catch and throw are not themselves generic. Plus, none of the interfaces used in the streams API have exception parameters.
> Plus, none of the interfaces used in the streams API have exception parameters.
That's partly because there's another problem. Potentially, you could have streams of the form, say, `...map(A::foo).map(A::bar).map(A::baz).collect(...)`, and each of foo, bar, baz adds another exception type to the set that can be thrown by collect.
Result/Either-based streams have exactly the same problem. An exception-supporting stream API would solve it the same way: a function can either throw no exception, or an exception of the same type as thrown by a previous operation. You could have a .mapException operation which could change the exception type, like Result::map_err in Rust.
In practice, you would end up with the stream throwing an exception of some general type (in 95% of cases, IOException!), but you could still write specific catch blocks for each possible subtype.
Let me put it this way: the message I got from the language team is that the problem is certainly acknowledged, and various alternatives to dealing with it are known, but none of them has so far been shown the clearly preferable, "good" choice, so until one presents itself, the decision is to do nothing rather than add/remove features that could later have other negative consequences.
Maybe that was it. That means there's no way to generically remove or translate the exceptions, e.g. by wrapping it into a runtime exception, or into a specific Either value based on the exception type.
Yeah, I'm pretty sure there were some compiler bugs involving generic exceptions around 1.5 - 1.6. (I don't know if they ever got fixed -- I switched to Kotlin.)
The big difference between the two is that Either/Result are trivial to compose and otherwise deal with in higher-order functions. Java has a massive problem whereby any API that invokes a callback has to either require that said callback not throw anything outside of a given short list of exceptions; or else declare both itself and the callback as "throws Exception", and force the API caller to deal with that - even though the specific callback that caller is passing in might not be throwing anything, or might be throwing a very specific exception only.
iirc scala literally began as just java with pattern matching from odersky's frustration from building javac in raw java. pattern matching is well on its way in modern java, with switch expressions and instanceof binding in jdk14 preview and more general destructuring (further enhanced by records) and guards up next.
in reality though aside from the rock/hardplace of language conservatism/fanatical back-compat i have to imagine a real reason modern java has yet to fully embrace Optional/Either over null/exceptions is lack of value types - yeah escape analysis lets you ~mostly~ not have to worry about the IdentityObjects you're returning everywhere, until you hit something it can't/won't inline or try to stuff those Optionals on heap and wind up making our already painful pointer chasing situation ten times worse. value types are also well on their way via project valhalla but have yet to actually land.
maybe ironically one of the things i recently got bitten by was the fact that MethodHandle .invoke* methods throw Throwable when used directly in plain java - the very machinery that enables the efficient composition of these kinds of functional programming styles is gated behind having to catch literally anything in the language itself. i guess the recurring theme here is java putting the horse before the cart, and that's frankly my favorite thing about the ecosystem.
I loved checked exceptions too, but the ergonomics of Either<Error, Result> works much better if you're writing code in a functional style. I even wrote my own Either which was a fun exercise.
Checked Exceptions are the main reason I switched to C#.NET. I hated checked exception. I know the designers wanted to save us from ourselves but they ended up making the language verbose.
Frankly, I find Kotlin syntax a lot more meaningful as it contains much less boilerplate code than Java. And the same time, though, I believe Kotlin can only be truly appreciated if you are coming in from experience with Java.
The thing I found hard at times with Kotlin is that it allows/relies on Java classes/frameworks but almost all of those classes/frameworks have their documentation (and only resources) written in Java so you have to have that additional mental step of translation between the two languages.
If you are not competent with basic Java, you may struggle to utilize those Java classes in Kotlin effectively.
Finally, when it comes to hiring people/finding job for Kotlin development, outside of Android jobs, there are very few positions requiring Kotlin which in terms means that there are very few non-mobile developers working with Kotlin as compared to Java. Now this may not be that much of a concern to us Developers, but for Management and leadership, it can raise a big concern towards adopting a language which from perspective of labor market comes across as a niche.
Having said all this though, I have no doubt at least in Android development, Kotlin will replace Java as programming language of choice but I don't think it will find traction beyond mobile (to displace / challenge Java).
I think I can confirm your sentiment. As far as I can tell, which, however, is not much as pertains to the JVM, apart from polyglot projects, Kotlin is a rarity.
I'm rather unfamiliar with Kotlin as a language. Is Kotlin for Android rather idiosyncratic as is Android Java? In other words, is there a superset of language features in Kotlin that goes beyond Android and is more expressive than modern Java?
It's actually not that rare, in my experience. Job adverts can be quite misleading in this case. Kotlin jobs are usually advertised as Java jobs. For a couple of reasons:
Firstly, quite a few large shops have started to use it, but usually due to grassroots bottoms-up activity of the sort that takes a while to percolate through to recruiting departments in distant departments where changing a job req is a heavy process.
Secondly, there's really not much incentive to try and change job reqs like that because it would be extremely hard to advertise via job reqs We use Kotlin but you don't have to know it to apply. To a non-technical recruiter or HR department this looks like a useless statement and will frequently get "fixed" to what you "really meant", something like "Java experience is required, Kotlin highly desirable" which might just put off candidates who don't know what Kotlin is or how easy it is to learn. You're just restricting your pool of candidates for no reason in a highly competitive market.
Much better to advertise the usage of Kotlin in places where developers looking for new opportunities might notice and leave the job reqs alone, and let Java devs get up to speed in the first few days. For example:
Edit: To answer your later question, yes, Kotlin goes beyond Java in a lot of ways other than nicer syntax. For instance it has a different (better) generics model, it has far more type inference, it has sealed (sum) types, it has language-integrated coroutines, it has compiler plugins that automate, amongst other things, serialization, it has delegated properties and interfaces, it has type aliases, it has extension functions etc.
I suppose a lot of these can be considered "just" better syntax than Java (except for coroutines and generics), but, at the heart of most programming languages is making patterns that are possible in other languages much easier.
There is no special Kotlin for Android - it compiles to JVM bytecode and works just as well on any JVM as on Android.
It has lots of language features that make it more expressive than Java (try doing constructor dependency injection in both!) but these are not specific to Android.
Are those the only parts of the spectrum that exists? Passionate and lazy?
I'd say that sentiment is lazy. There are lots of reasons why someone wouldn't or couldn't spend time learning Kotlin (kids, other family, other hobbies, list could go on).
result = Optional.ofNullable(nullableVariable)
.map(NullableType::someMethodCall)
.orElse(fallbackIfNullMethodCall());
Sure, it's no Elvis operator, but it's a lot more readable than the "standard" if/else structure of traditional Java. If we're comparing Java and Kotlin, we should at least give Java a fair chance. I dislike the verbosity of the Optional wrapper, but it's a lot better than if/else checking.
My code contains a bug, the code driver733 wrote is what I intended to write (usually I use predefined variables for orElse, conditionally taking the method call threw me of guard).
the intension is that the Optional reference is not null (despite it being _possible_ due to how references work in java). And it's fairly easy to add annotations like @NotNullable or some other compile time annotation which can be checked very easily by an IDE or other tool.
Just to play the devils' advocate, what happens if you want to represent the absence of having a value? that is, the Optional is not None (because it hasn't been computed yet).
Is the actual logic different in Java vs Kotlin examples?
In Java: when `nullableVariable` is not null and someMethodCall() return `false` then we return what `fallbackIfNullMethodCall()` returns right?
and in Kotlin in that case we just return `false` from `someMethodCall()` because this is not null and `fallbackIfNullMethodCall()` is not even evaluated?
This doesn't really showcase many of the best advantages of Kotlin. They should add some with data classes, JavaBeans, multiple assignment, lambdas, map/reduce/fold, extension methods, etc. I've found that Kotlin code is usually 3-4x more concise than Java, and most of the benefit is in your data structure definitions (i.e. changing 100 lines of boilerplate to a 4-line data class, or destructuring a Pair/Triple where you would otherwise have been tempted to create a new class and 2-3 lines of assignments) rather than in individual statements.
Kotlin (which I don't know) looks a lot like Scala (which I do), at least superficially. Wonder if you've had any experience with Scala to compare the two?
I did Scala first, then recently took over a codebase with both. I think the people who did Scala were not good and they made a total mess, however, their Kotlin miscroservice was actually quite nice and was the best code in the project. So I reluctantly have concluded Kotlin is a better level of expressivity. Scala is full of footguns... I love it ... but it's hard to write well.
One example was they were using enumerations and got boxed in. They had to add unit tests to ensure they enumerated all the options. Why does Scala have that feature? It's full of bad features you should avoid.
I have the same experience. Once you do more with Scala than "better Java" it is easy to create a mess full of "try to be clever" code you will regret a year later.
It is really hard to become a good Scala programmer, but it is not hard to become a good Kotlin programmer. All the little things that make Scala theoretically better are not worth the learning effort. In Kotlin you get 80 % for 20 % of the effort.
Scala is separated in subcultures. Some developers use it as "Haskell for the JVM" and other as a "better Java", or something between. That makes hiring and picking up existing projects hard.
Tooling is another weakness of Scala. Scala IDE (Eclipse) is more or less dead. IntelliJ with the Scala plug-in is ok, but the Kotlin support is much better.
I have been using Scala in Production for 5 years - microservices to streaming to batch jobs. I recently started working on Kotlin. Kotlin is the better Java than Scala and is much easier for someone to pick up. And like you mentioned, Scala is very easy to mess up. Scala the good parts is tough to initially identify and work with, and one might as well pick up Kotlin if they are not into functional programming.
To sum up my experience: Scala can be even more terse than Kotlin, but I found Scala code generally harder to read and understand compared to Java. It has the kitchen sink thrown in in terms of features and the idiomatic way of writing code is pretty complex and very different from Java.
Kotlin on the other hand is just a breath of fresh air, very easy to pick up and get productive.
I just hope they keep the language to a manageable size and don't keep putting things on top.
Used Scala in production for two years and have been writing Kotlin for a year now.
I'd still prefer Scala to Kotlin any day as I still think it's a stronger language. We had to use arrow library to make Kotlin more like Scala. Especially if you're leaning more functional, standard Kotlin will be lacking.
That being said, Kotlin's entry point is much much lower for Java programmers. Kotlin is a better Java, so I can understand the appeal. I'd never start a new project in Java when Kotlin is available.
Kotlin is less orthogonal than Scala. For example nullability in Kotlin is a special language feature that can't be abstracted nicely, when in Scala it works just as any other monad.
Also I don't agree it is like Rust or Closure. Kotlin got majority of features from Scala, and contrary to Rust or Scala it does not offer anything innovative. Kotlin is just Java with nicer syntax. Scala and Rust are more complex, but these are the few that move the needle.
I've developed in Scala for 5 yrs, last yr in Kotlin.
Scala spearheaded from 2007 a lot of features that nowadays are the norm for statically typed programming languages. Odersky is a visionary imo.
For a while I experienced Kotlin as Scala--. (No pattern matching?!? )
A bit later I discerned where Kotlin is treading its own path. The biggest things for me are: non monadic approach to async. Which works really well. And simpler extension functions.
I'm looking forward to trying out union types that are coming to Scala.
I have been extensively programming in Kotlin in the last 2 years coming from a 10 years Java experience. Even Java advanced a lot from from version 8, still it is very far behind in reaching the pure joy of programing in Kotlin, especially in functional style. Immutability with 'val', data classes, functions as first class citizens, destructuring, if/when expressions, just to name a few of the features than enables this.
Also, the great 100% interoperability with Java makes possible to migrate part of your codebase to Kotlin and using tons of already proven and great libraries in the Java ecosystem. On top of the Kotlin standard library includes many improvements of the existing Java classes from collections to String, and all of this using extension functions, one very usefull and powerful feature.
I can keep going on and on, but to conclude Kotlin is not just Java with improved syntax, but completely new modern programming language incorporating or "borrowing" some of the best features from Scala, C# and other popular programming languages. So in my opinion it is a great language which has a lot more funtional features than Java, but still pragmatic and easier to learn than Scala.
I was mostly referring to the Kotlin calling side of Java code and libraries, and yes you're technically right that would mean only 100% compatibility with existing Java code and not interoperability. But, when moving forward to using exclusively Kotlin and not building libraries that will be Java compatible that is totally ok.
Kotlin is not moving forward on the JVM given that the platform is written in Java, and Kotlin happens to be a guest language, now with JetBrains trying to build a Kotlin platform.
For sure, declaring something as `val someReferenceOfMutableObject` will not make the object itself immutable, only the reference. The case I had in mind was when creating class with only `val` fields, and that would certainly make that class immutable.
At it's base Kotlin is a nicer java. But it's much more. Simply looking at the syntax comparison is only half of the story. The focus at the recent conference was multi platform and concurrency.
They are making concurrency a first class citizen. With items like Go channels, Observable etc. This is baked into the language. There are a number of new frameworks utilizing these patterns. Resulting in less code, and more performance. At the cost of sometimes more difficult debugging.
The bigger item to me is multi platform. I'm working on a micro orm. That reflects your schema automatically generating types for NodeJs, Native, and JVM(Graal/JDK). It can then be run on any of those platforms. An example is libpq for postgres allows streaming and observing rows on update. This is not in the DB libraries that I've seen yet. I can have an actor run natively on LLVM, talk back via grpc/rsocket/tcp/etc. To a JVM/JS service. All while adhering to the same interface / method description.
Besides that. There are scripts for kubernetes, react/angular/vue, etc. Dukat is coming to automatically convert ts.d files to kotlin description files. For me I can have one language, one IDE, for every part of my product. From deployment, back end, data handling, front end, and mobile.
Thread based concurrency where everything is blocking inside the thread is the first class citizen. If you want to move to a non blocking world, then you will suffer.
Moving between Channel and Flow sucks. No async/await keywords like Python, C#, Rust so you have callback hell. try-with-resources doesn't work with async callbacks (i.e. thenApply, thenCompose, etc.).
Also, JAX-WS standard doesn't currently support Channel or Publisher<ByteBuffer> for body types.
Literally "when it's done". This is the answer given in talks by the developers working on it.
But you have to understand that many many libraries are on Java 1.8 because that's what Android supports. Even when Loom arrives, many libraries won't use it but will rather continue to use threads.
But that's the nice thing about Loom. The direction they're heading in is upgrading the existing Java threading APIs. Maybe not "new Thread() {}" but if you use ForkJoinPool, executors, etc then it should mostly all just work. That means it won't fork the ecosystem in the same way Kotlin coroutines do (coloured functions in new languages). And even better it won't fork the ecosystem across Android either. If you use constructs that don't automatically Loomify, you can just tweak them so they still work fine on Android but get the benefits of Loom too.
BTW parts of Loom already shipped.
On the other hand, outside of certain core libraries this won't matter much. You don't do a whole lot of scalable blocking IO on phones.
Whenever I see an article comparing a market leader with an underdog, I always know that the article will be biased against the market leader. It is human nature to root for the underdog. What was the reason for not starting with variables II? I suspect the author wanted to manipulate our minds right there. It is obvious that Java version is the better of the two but they decided to start with a variable with final. Ternary operator for Kotlin doesn't look like ternary to me. It is just an inline if\else.
Why is variables II better in Java? Its almost the same with advantage on Kotlin side with type inference. Also ternary operator isnt needed in Kotlin since if/else itself can return value, so its just example to show so people dont try to look for it in Kotlin. Overall its not even an article, its just a showcase of differences in syntax and idioms in both languages, i cant fathom how could you be manipulated with that.
Clear and concise to someone new to programming. Also less typing. Comparison of an a market leader to a new entrant is designed to make you switch from market leader to new entrant in the market.
They explain it here[1] on their discussion forum but still it doesn't make sense to me. If is a statement in every language I know and returns a value, but still those languages have ternary operator. I suspect they don't want to end up with a language similar to what we already have. They want to be different. Even when it isn't necessary.
Ternary operator is a C-ism. It's common in C, C++, C#, Java, PHP, Swift, Javascript, ActionScript, and related languages, but not present in most of the rest of the PL world. 'if' is an expression in Lisp, Haskell, ML, Scala, Ada, CoffeeScript, SQL etc. A modified form of the if-statement is an expression in Python 3 and Ruby. Go and Pascal have no related concept.
What's the difference between ternary operator and inline if/else? Is there even any reason for if/else that doesn't return a value (that may be void/unit/similar)?
It is also obvious that Java version of the variable syntax is awful and just asking for unintentional modification.
> What's the difference between ternary operator and inline if/else?
There is no difference. Ternary operator is a syntactic sugar for if/else.
> Is there even any reason for if/else that doesn't return a value (that may be void/unit/similar)?
Main reason why an if statement returns a value is because it is used to test if condition is true or false. If it returns other values, it was going to make it more complicated. Especially when you have an if statement with multiple conditions that must be ORed or ANDed.
What is awful about the variable syntax? This syntax was copied directly from C language.
> Main reason why an if statement returns a value is because it is used to test if condition is true or false.
Uh, no. The condition itself is true or false. The return value is useful for assigning, passing as parameter, etc. Non-returning if requires dealing with temporary variable, which itself is verbose, doesn't automatically get assigned in both branches, doesn't play nice with C++-style move semantics, etc.
> What is awful about the variable syntax? This syntax was copied directly from C language.
C language is awful, but at least they have the excuse that nobody knew better in the 70s and const kind of works even the syntax is a bit complicated.
It is, however, even in the scope of the "Hello world" app, Kotlin performs much better than Java in terms of bringing out business logic to the front.
This, some people underestimate how good Java/C# IDEs are and how much typing they save. I love how I can select a piece of code and do something like "extract to class" and it will create a new file, create a class there, put the code in the method and add a class method call at the original spot.
Kotlin is much nicer than Java, that's for sure. It strives to be compatible both ways (calling Java from Kotlin and vice versa), which I think has helped with its adoption. It also means that it cannot have very wild features that can't be mapped nicely to the world of Java, which I think has its downsides.
My main gripe with Kotlin is some of its design choices for syntax. I really really really don't like its syntax for lambdas, specifically. A code block in curly braces could be both a lambda with an implicit argument, or a list of statements. You have to inspect the context to see which one it is, or rely on intelliJ to make the former type slightly bolder. That is so bad for comprehension, and I think the only reason it was done like that was to allow for some cute hacks where control flow constructs such as map, fold etc. could be implemented using lambdas and still look like regular imperative code.
Another thing is that you can create lambdas with an implicit binder that you can refer to as "it" inside the body. It may save you one or two keystrokes, but it also encourages the programmer to be lazy about naming things. Combined with the fact that curly braces are overloaded, whenever I see "it" in code, my eyes scan frantically for the first opening curly brace, and I then have to determine whether it is a lambda with an explicit binder (then it isn't my binder, and I have to continue searching), a statement block (then it also isn't my binder), or a lambda with an implicit binder. Don't get me started on nested lambdas with implicit "it" binders that shadow each other.
Well, they see it more as a core language feature than a cute hack. Kotlin 1.0 was big into DSLs as a competitive advantage, though I don't think it got adopted in the way they hoped and I don't see much talk about it anymore. But the idea was you can make libraries where you can write code like this:
val page = html {
head { title = "Title" }
body {
etc
}
}
It looks like it's a part of the language and naturally integrates with features like code folding, but it's actually all just functions and objects anyone can create.
DSLs have taken off in one area: UI construction. There's TornadoFX for JavaFX, SwiftUI on Apple and JetPack Compose for Android. I've tried both new and old approaches extensively and to be honest I'm not sure this is actually better than using a proper GUI builder, but for small fragments of UI it's pretty neat. Sort of like what JSX does in React but more principled and type safe.
I don't see why you can't create DSLs with an equally compact syntax which doesn't overload the curly braces. Yes, it would look different, but it would be more readable. Curly brace lambdas give a first impression of readability, but they really are not when you consider how they affect the binding structure.
the biggest wins w Kotlin, imo, are the sensible defaults - variables are non-nullable & collections are immutable by default. These eliminate entire classes of runtime exceptions and bugs (respectively).
The Elvis Operator is particularly useful because it's such a common pattern in Kotlin, since the language enforces null safety. When you're using a legacy Java API without nullability annotations, the language forces you to explicitly handle a null result.
It masks the complexity of your statement. I know in ruby we had the same issues with the “try()” method. Our rule was, do or do not, there is no try. :)
The beauty of the ternary operator is that it isn't one. It's just taking advantage of the fact that if/else is an expression in Kotlin and it turns out that when if/else yields a value, a dedicated ternary operator is redundant. It's one of a small number of things that I like better about Kotlin than Groovy.
Rust has this, as well as some other nice ML-inspired[0] features, like structured bindings.
That’s one thing I like about Rust: it fuses functional and imperative styles very naturally.
[0] “ML” here meaning the family of languages including SML and OCaml, which inspired (to varying degrees) Haskell, Rust, some parts of modern C++, and surely others. Not “machine learning”.
Nim does this as well. Almost any block statement (including creating a new scope with `block`) can be put on the right hand side of an assignment. Whatever value is returned from the block is the result of the expression. All lisp inspired, of course, but with a Pythonic syntax.
On the contrary, I consider the "Elvis operator" to be one logical operation: give me this value, or the other one if this one is invalid. What's the more terse way to write the example with a ternary operator?
You're missing the fact that the constant factor differs between languages. The density (bugs per LOC) of memory handling bugs is much different in Rust than in C.
> The quote is either wrong or outdated. In the second edition, it's on page 521: "Industry average experience is about 1 - 25 errors per 1000 lines of code for delivered software. The software has usually been developed using a hodgepodge of techniques.
That's true, and Kotlin definitely leans into it, but extremely concise code basically ends up looking like Brainfuck -- lots of symbols jammed together and hard to read.
There are very few of these examples that don't just make me feel exhausted compared to the Clojure version, but I realise it's a different set of priorities entirely.
I love the idea and look/feel of Kotlin. I work to learn and use it on my own.
In the workplace, however, the problem for me are the fearful developers who think it is too difficult to maintain a multi-project code base that contains more than one language. I want to continually grow and improve and branch out, sadly I find many I work with who would rather just skate by on what they know (in this case, Java, which I helped to mentor).
I haven’t yet found a way to convince them. I feel the only way I’ll be able to use Kotlin within the workplace is by changing jobs.
Java is still a great language, and the new release model is helpful to get some of the more modern language features found in other languages sooner in the Java ecosystem. And while JEP is a fine governance model for feature development, I still find myself wanting more, sooner, than what it produces.
I welcome any thoughts or suggestions on how to convince others of the usefulness of introducing Kotlin into the workflow. The idea I attempted was that I would begin a new project with Kotlin that, to start, only I maintained — to make it easier for others to begin to absorb the language, at their own pace. It was futile and they simply don’t want to try.
I don’t know why but your assumption that others are skating by with what they know rubbed me the wrong way (maybe because I have been on other side of this). There are likely many other valid reasons here - project deadlines (sometimes it is not the right time to pick a new language), consistency of code (having a mix of languages is going to raise hell in code reviews with opinions on some code that could be done better in one language vs other), hiring (does your hiring/onboarding get more complex?), writing idiomatic code (learning a new programming language is easy but writing good code is hard), extreme swing in brevity (people abusing new languages to write concise code using obscure language features, which makes it hard for someone new to jump in).
I am sorry I don’t have any suggestions to fix your problem but I just wanted to provide a bit of perspective. Maybe try thinking from your peers perspective to see what benefits adopting this new language would give and be prepared to answer why Kotlin among a whole class of JVM languages - Scala, Kotlin, Groovy, Clojure.
It’s a very small company and I’m the lead developer. I know exactly what is being worked on and what timelines are involved. The replies I got when I suggested all of this was something akin to “Why now and not before? Why this project and not the others?” Even though the majority of what we work on was originally developed prior to Kotlin going 1.0 and I had already laid out my reasoning. It felt like excuses due to simply not wanting to learn and maintain something new simply because they didn’t see any benefit, even though I also explained the benefits in my original suggestion.
Thanks for your thoughts, though, I’ll take them into account.
OK, but where are the advantages? The page has just a different syntactic sugar resembling Pascal/Scala; sine JVM nihil Kotlin anyway. Sometimes Kotlin's "advantages" there have more boilerplate than Java...
I leave it to the reader to decide whether the advantages are clear or not.
For instance, in Kotlin you can perform operations on collection without converting them to a stream first, and collecting them with a certain collector afterwards. So in order to perform a single filtering operation on a collection, you need to do three steps in Java and just one in Kotlin.
Another advantage is coroutines, which not only allow you to write asynchronous code in a common, synchronous manner, but also get rid of shared mutable state, since coroutines can send/receive shared state through channels.
That's actually what I like about Kotlin the most -- it's just syntactic sugar over Java, I wouldn't call it a new language.
E.g. if you want to parse a CSV or JSON, you would google "how to parse it in Java", not "... in Kotlin". That's different, from e.g. Scala, where everything should be Scala-way (of course you could use Java libs in Scala, but it was always second-class I feel).
Once you learn the Scala way of e.g. parsing JSON or defining any custom parser or working with collections/optionals you don't want to look back. I had to move back to coding in pure Java for a while after coding Scala many years, and really it feels Java is making things far too overcomplicated in many places.
It is only because you are familiar with Java. Scala is much different language so it looks unfamiliar to you. But familiarity is not objective simplicity. Java has many more gotchas and irregularities than Scala, and you just got used to it, so you don't see it. Primitive types vs boxed types, static state, default methods (poor man's traits), unsound array types, etc to name a few.
Also it is funny to see how Kotlin or Swift are marketed as simple languages when they managed to literally copy 90%+ of Scala features and make some actually even more complex (eg nullability).
If you say "overcomplicated" it suggests you have a simpler solution for the same problem in mind. So, what exactly would you simplify in Scala? Which unnecessary features would you remove? It is easy to throw an "X is overcomplicated" statement but much harder to point to concrete things that could be done differently. Sure, some stuff could be easily removed, but not without making the language less expressive.
Look, Java was born when C++/CORBA dominated investment banking and everybody was having headaches with those. Java was like "manna from heaven" that made everything orders of magnitude simpler. It still didn't get everything right, like too much boilerplate, but simplicity of approach was there. Then Scala arrived, and obfuscated everything. It didn't retain simplicity of either Java or Haskell, instead went full-blown C++ "many styles" way. As a consequence, either all of your team is on the same level in the same areas in Scala, or you are going to have "fun" in production. In Java the syntax was quite trivial, in Scala one has to fight with DSLs of different frameworks and unintended consequences of various higher-order structures originally designed to be "simple".
I think an issue is a different mindset. I haven't done OOP for a long time. After Haskell, Scala feels a bit verbose and actually rather complex. I had a go at Scala a few years ago, it felt like a breeze after Java and C++. I think I can confirm your perception that that's a question of familiarity.
I'm now embracing the JVM, and Frege is not an option. It'll be a mid-term transition for me. But it's already nice to rediscover some functional paradigms in Scala and apparently sane design choices when it comes to mixing the object-oriented with the functional style, at least I think I like it better than OCaml.
I'm bothered by Scala's performance as compared with Java. Kotlin's seems to be in parity with Scala in this context, but both by an order of magnitude off from Java's. However, I can't vouch for the "benchmarks" I've come across and I'm not yet familiar enough with either to conclude which features could be detrimental to JVM's performance.
I would wholeheartedly appreciate your take on that.
Its really amusing to consider Java somehow simpler than Scala. Take any Java class and rewrite it in Scala, you end up with at least 3x fewer lines of code.
That's not about length of code. It's about how many different approaches in Scala one has to be aware of when working in a team. All team members need to have a similar familiarity with the language/frameworks or team disintegrates and is unable to fulfill its goals. Scala is as fragile as C++ here, prolonging development time unnecessarily.
Same is true for any other mainstream language like Java, C# or Python. There are many ways to do the same thing, and this is why teams have code style guides and code reviews.
Last time I tried to store items in a collection and iterate through them (List in Java), I counted 11 different types I could call it in Scala (List, Seq, SeqLike, Traversable etc). And different tools.used different ones. It wasn't easy.
I could easily find 11 collection types in Java as well. Any industrial language will have many data structures, because they have different tradeoffs and there is no single container that suits all needs.
Scala standard library is a bit in the baroque side, but it has been simplified recently in 2.13.
Java had a native gcc front end way back in the 90's. No one used it and they eventually dropped it. This isn't really a killer feature for the problem areas targetted by Java developers.
Android ART is, effectively, a native compiler for a Java-targetted (but not JVM) bytecode too.
I think one of the main use cases of graal native will be for container's with smaller memory footprint (where GO traditionally shines) or where startup time needs to be fast.
This is correct. It's a proper compiler (on top of llvm) and when you use it, you can't use Java classes or libraries. Instead, Kotlin has a notion of multi platform libraries with e.g. serialization, http, and IO libraries that you use. Short term this is aimed at mobile developers looking to build libraries that compile to IOS and Android. Long term this will likely pop up in other places as well.
There's also a javascript transpiler and tools to adapt typescript definitions so you can use Kotlin for react work or node.js work as well.
Kotlin Native predates the release of native-image. Also it's actually a slightly different language. Kotlin/Native really wants you to not write shared memory multi-threading. All variables are thread local by default. It has special constructs for interop with C libraries, and uses a sort of reference counting Python-esque memory manager if I recall correctly. You can only use C or other Kotlin/Native libraries.
GraalVM native images are a different matter. It uses the Java memory model and ahead of time compiles all the code to native. The "VM" part is bundled with the binary but it's very small and thin. Really it's just a simple generational GC and a bit of support for reflection over things you said you want to reflect over.
Both produce small binaries that start as fast as C programs do. In fact the GraalVM team have advertised cases where native-image binaries start faster than C programs do.
Yes and no. It's "like Graal" in that there is a Kotlin/Native runtime which is built into the binary. It's "not like Graal" in that it is not building in the entire JVM, and K/N code provides things like manual memory management APIs which are not available in e.g. a Java->Graal context.
Which could be considered a reason to use Kotlin instead. Scala is a more powerful footgun. You pay for the power in overengineered code, slow compile times, and worse compatibility with other JVM languages.
Scala compatibility with JVM is the same as Kotlin's. It is implemented the same way in both languages - they compile to JVM bytecode and as long as you keep away from non-Java features, interoperability is good both ways. If you start using stuff not directly supported by Java like default arguments or multiple inheritance of traits, then calling Scala/Kotlin code from Java is similarly bad experience. Scala has also a few more non-Java features e.g. macros or implicits, so I get how you could end up with this impression. Just don't use these features of you need to call your code from Java.
As for compile times, Scala with Bloop is the fastest of JVM languages I ever saw. We switched to compile Java with Bloop because it is orders of magnitude faster than Maven/Gradle. Bloop works for Java and Scala, but not Kotlin. So Kotlin, having really nothing comparable at the moment, is currently the slowest to compile from these three languages.
Something doesn't add up. First you say (emphasis mine):
> Scala compatibility with JVM is the SAME as Kotlin's.
Then you follow up with:
> Scala has also a few more non-Java features
These are contradictory claims. OP had a very good point about Scala being a more powerful footgun. Unfortunately, consciously avoiding features is not a matter of "just", and rarely works out in practicality.
No they aren't contradictory. What is the compatibility of a feature that even doesn't exist in Kotlin? How can be the lack of way to call a Scala macro from Java a disadvantage, when Kotlin doesn't allow you to call a macro from neither Java nor Kotlin, because this feature doesn't exist? Scala is strictly superior in this case.
And you need to avoid some features only in code that gets called from Java, not the whole codebase. Calling it this direction is very rare and not really a problem. Most codebases call Java from Scala and this is easy and fully supported.
But Kotlin doesn't have 100% interoperability! There are features in Kotlin which don't map nicely to Java features. For example you can't call methods with default arguments from Java without resorting to unreadable hacks relying on a way defaults are implemented (same is true for Scala).
I've inherited a large scale codebase and it's been interesting to put it mildly. I agree with GP that Scala's power lends itself to building unnecessarily complex systems.
- Scala has a lot of cool, but sometimes confusing features. Implicits, companion classes, apply/unapply.
- our compile times are 5-10 minutes in CI. I'm sure we can make it go faster, but it requires digging into a 500 line SBT file.
- Scala seems to encourage building tangled webs of actors with Akka. I understand the benefits of actors. I think the benefits are overstated vs threads when you need less than 10k threads or so.
All of what you're describing here is bad programming practices. Bad programming practices * scala's power = Incredibly bad work.
If you're a good programmer who writes well engineered code, you can do that incredibly well in Scala. Good practices * Scala's power = Great productivity + really good work.
Companions are in Kotlin too.
Apply and unapply - I never saw this being a problem even for some new programmers switching from Python.
Implicits are kinda harder to use, but this is an advanced feature meant for library writers, not application programmers. Despite their power I find them much nicer to use than c++ templates. And there is nothing comparable in Kotlin, and insufficient abstraction can sometimes lead to actually more complex code than in Scala.
The Kotlin creators were looking to create a modern language without some of the issues that Scala has. Judging from its rapid growth in popularity, it is doing a few things right.
I think its growing because its the only way to do Android dev if you don't want to do Java 6. For that, its still great, but for any backend programming, you should consider getting the real thing (Scala) and not its watered down copy.
Code bases with Scala tends to be unreadable for people not well versed in the language. Kotlin is mostly just a better Java. So it's easier to introduce. And the interop is first class, so it's really easy to mix and match java and kotlin, which also makes it easier to gradually introduce.
> unreadable for people not well versed in the language
This is a sign the language is pulling its weight. We're going to spend most of our careers at the top of the learning curve, so we shouldn't optimize for briefly being at the bottom.
My experience tells me the opposite. Scala codebases written by professionals are very readable even to people not knowing Java, but Python. Many analysts use Scala and Python. The only people I met who complained on Scala complexity were guys who were either invested heavily in another language (eg Haskell), in which case it was just a subjective opinion or people who struggled with writing good Java code as well.
I think Kotlin is outgrowing Scala on the backend as well. Spring is basically not bothering at all with Scala but they are adding lots of Kotlin specific functionality in the last two years. Check this recent presentation of the state of Kotlin in Spring: https://www.infoq.com/presentations/spring-kotlin-boot-kofu/
If you read between the lines, what they are saying is that Kotlin is now the primary language to use Spring with.
Absolutely untrue, Scala is completely backwards compatible with Java - you can use all your java code in Scala and even write scala as mostly a better Java.
Sadly it is actually the case. I recently worked on a project using the AWS SDK. We had written our code using Scala and had to up version the AWS SDK to 2.0. Because we used reflection with it, we encountered a Scala compiler bug (an illegal cyclic reference) and had to rewrite it all in Java.
I tried Kotlin for some time and went back to Java.
Kotlin is nice, and on some days preferrable, but the fact of the matter is, it's really not that much more legible or succinct than Java in the long run.
Look at Androind BTE docs [1] - the K vs. J examples side by side, there really isn't that much difference.
In the end, I feel K is just a 'different flavour' of Java, and often I feel that the makers of K are just throwing a bunch of ideas together, without necessarily having a philosophy.
Given a choice between a 'world of J or K' - to me, it's kind of a toss up - but in reality - J is the incumbent. You have to know J and deal with J at every step, all sorts of libraries are in J etc. etc. - so in reality, the offer of Kotlin is really not Kotlin, it's always Kotlin+Java - meaning two languages and paradigms to support.
If this were 1996 and we had to pick between DVD and BlueRay, it would be a difference decision, but in the end, it's not really 'either or', it's J or J+K.
In the end, J is not that much more hard to write and the differences are negligible except for some specific things ... so I've switch back to J and don't miss K.
I wonder whether Project Loom will create a split in the Kotlin ecosystem between coroutine-based concurrency and Loom lightweight thread-based concurrency.
Java will have a similar problem with CompletableFuture and third-party projects like Reactor used in Spring WebFlux.
However, Kotlin baking in language syntax for a competing concurrency model will exacerbate the split.
With Java already having type inference, concise lambdas, and with upcoming records that can already be faked in current versions via Lombok, the value proposition of Kotlin seems questionable outside of Android with its held-back version of Java.
Clojure is another story entirely, offering a very different and compelling way of thinking about computing on the JVM. By contrast, my developing view is that Kotlin is different enough to require learning something new yet not different enough from modern vanilla java to offer substantial advantages.
I think one feature of Kotlin that is underestimated is extension functions. Combine them with autocompletion and you've got a great developer experience. Finding the method you need no longer requires a Google, and optimisation pays off massively in terms of productivity by tightening the development loop.
I like Kotlin (as a half-way house in a Java codebase to something purer like Scala) but I'm worried about being locked in in future, and that becoming a liability in a codebase 20 years from now.
Does anyone have experience of emergency automatic conversion back to Java?
(Edit: Never used Kotlin but I like the look of it)
I think the reason a Kotlin to Java converter does not exist is mainly because there's simply no demand for it in the Kotlin community. In my experience it's a one way street and people generally have no desire whatsoever to go back after they start converting Java code to Kotlin. There's nothing particularly hard about it; it all compiles down to the same bytecode.
People keep bringing up Scala as a "purer" language. This seems to be true in the minds of Scala purists mainly. I've done Scala, IMHO it's a very messy language that seems to result in unwieldy code bases that was made for wannabe functional programmers that need a halfway house on the way to a proper functional language (like Haskell or Elixir).
There's no direct conversion back. IntelliJ has a decompiler that produces reasonable-ish Java code based on the debug symbols, so you get things like local variable names and obviously method/class names back, but, it's a decompiler, so no code comments. And Kotlin constructs frequently require synthetic Java variables to be created that were implicit in the Kotlin, so you have to clean up the result quite a bit.
However note that Kotlin and Java code can co-exist very closely in the same codebase. In the same way you can incrementally port a Java codebase to Kotlin, so too can you incrementally port Kotlin to Java. There's just no automatic tool for it like there is Java->Kotlin.
Locked into Java, yes. At some level it's unavoidable, you have to choose some kind of stack. And of course there are different kinds of ways to get out of lock-in. One of them is being able to convert to a common 'lower' language.
I've not used Kotlin but I would be more likely to if I knew that avenue was practical.
All of this stuff will eventually wind up in Java, and when it does, Kotlin is going to feel an awful lot like Coffeescript. The Kotlin proponents of today are going to feel a bit silly.
I predict there's going to be a lot of effort wasted porting old Kotlin code to Java 20 or some such.
But all of this stuff simply can't end up in Java, because of backwards compatibility. For example, kotlin object references are non null by default. That is not something Java can easily bolt on. I think there should be many such features.
Having lived through the CoffeeScript thing, the main issues were:
1. CoffeeScript required a compile step; JavaScript didn't. It also meant that you needed to do your debugging in JavaScript, and deal with a lot more leaky abstractions. On the other hand, Kotlin and Java both compile directly to JVM bytecode.
2. CoffeeScript had a lot of features that work very unpredictably, like a number becoming an array of numbers if you add more lines at the same indentation level (which also means that your array will surprisingly become not an array if you delete too many entries). A lot of people only used CoffeeScript because there was no alternative, rather than actually liking it, and they were the first to jump ship when ES6 gave them an alternative.
Kotlin doesn't have any of these issues. It really is an improved Java.
Certainly Kotlin vs Java looks a lot like CoffeeScript vs JS.
For one, CoffeeScript had great impact on JS, and still has a few advantages (along its disadvantages). Perhaps Kotlin will play the same role.
But I'm doubtful Java can evolve as JS did. For one, the philosophy and speed of development of Java is very different, and kinda set in stone. But perhaps more importantly, types are part of Java, and they need to be backwards compatible. I think this will make it extremely hard to adopt some of the sane defaults from Kotlin.
So choosing Kotlin over Java boils down to some syntactical differences? Nah. The syntax differences between Kotlin and Java are not even that huge. It's not like choosing between Elixir and Erlang, or between JavaScript and ReasonML.
The main differences between Kotlin and Java are:
* Kotlin forces null checks.
* Kotlin arrays are invariant.
* Kotlin does not have checked exceptions.
* Kotlin has first class functions.
* Kotlin sealed classes don't really have an analog in Java, AFAIK.
I don't find data classes to be a big deal. The best thing about them is you get `copy()` for free.
Sure, but you have take into account that Android devs are limited to Java 6, most enterprise devs (based on my experience) are still on Java 8 and some are still on Java 5 or below.
Yeah this is nonsense and isn't an argument against the language. Embedded devs aren't allowed to use Rust, does that mean we shouldn't compare C++ 20 to Rust? Does that mean Rust is a bad language? If these said devs are limited to Java 6/7/8 does their company have the inertia to use a brand new programming language (Kotlin) that their devs don't know? Does this mean Kotlin is bad because they're not using it?
My goal is to compare Kotlin with the current version of Java along with any Java libs that are commonly used (such as Lombok). I have mentioned the fact that some teams are still on older versions of Java just to highlight that not every developer can access the features of the latest Java release.
You can compile Kotlin to target any of these JVMs, so of course it's relevant. If you're limited to using Java 6 every day, Kotlin is a game changer. If you can target Java 13, less so.
These examples don't show the true power of Kotlin, for example pattern matching is super powerful and helps to put a much more solid and readable logic in your applications.
Still a lot of Kotlin I see from my Android colleagues suffers from all the CONSTANT_STRING_TYPED magic and onion architected* stuff either inherited from the libraries and API's or ingrained into their programming habits.
* many layers, and every time you peel off another layer you feel more like crying
A lot of this could be misleading, a programming language can have excellent syntax to do something small and yet might turn out to be a horrifying if you are going through a complex system.
Having worked with Kotlin I do love the language though. There are some features like extensions which I hate. But in general I love the syntax though I do not find any significant advantage of using it over java.
Very nice catalog of comparisons. IMO it could also use a section on "Generics," Kotlin's declaration-site variance is a nice improvement over Java wildcards.
To those who've done a lot of Kotlin, does it actually alter your programs' design and structure? Your approach to problem solving? Does it open new doors in how you choose to think about and attack a problem? In a nutshell, do you think your programs are better because you chose Kotlin over Java and why?
I've converted a lot of Java backend code to Kotlin. You usually start out with the intellij converter. I tend to go class by class. That leaves you with something that nearly compiles but not quite. After addressing that, you typically end up with some needlessly nullable types, lots of mutable types, some misguided generics and a few other java-isms that are easily fixed. So, far you've not really changed the structure of the code, you're merely improving it and making it more safe to use. I usually get the process above over in a few minutes for a single class; it's not hard and I would argue the resulting code is already massively better than what you started with.
After that, there are probably a few places where idiomatic use of Kotlin library and extension functions can improve things. I tend to that as I stumble on opportunities to do so. This stuff is great for getting rid of boiler plate. E.g. Kotlin's internal DSL language features can replace a lot of ugly builder pattern code. Also having constructors with default arguments removes the need for having builders or having multiple constructors. Same for functions.
Converting streams to sequences and restructuring statements with nested returns to return the result of a complex expression helps to. Using when instead of if makes things nicer. Etc. Lots of stuff to use.
My programs are definitely shorter in Kotlin and safer (e.g. nullability). Shorter is correlated with lower maintenance cost in literature on software engineering. Some things that Java is ok with are compile errors in Kotlin. So, safer is better in my book as well.
Out of topic, talking about language defaults, I wish C++ defaulted to const reference instead of value. True that it would lead to some pitfalls such as having to explicitly copy values during initialization, but it would make things much less verbose.
Kotlin is like a virus. Once you use it in a non trivial project, it infects you and takes over your body and mind. Every time I look at Java code in my Android apps, a part of my body hurts. Luckily there exists a Java to Kotlin converter.
Kotlin really shines when it comes to async programming. This is some code I wrote using Vert.x with Kotlin extensions :
router.get("/api/article/:id").coroutineHandler { ctx ->
val news = newsRepository.get(ctx.pathParam("id")) // no need for await
?: return ctx.respondText(404, "Not Found") // can still use basic code constructs
ctx.respondJson(200, jsonObjectOf("news" to news)) // actually an extension method i wrote
}
For fun, I'm going to rewrite all of the examples in Scala:
1) Main
def main() = println("Hello")
2) Variables:
val y = 1
3) Options (nulls are discouraged):
val name: Option[String] = None
4) Null 2:
//If bob is not null, get his age, or get a default age
val age = bob.map(_.age).getOrElse(0)
5) Elvis operator
Kotlin:
val result = nullableVariable?.someMethodCall() ?: fallbackIfNullMethodCall()
Scala:
val result = option.map(_.someCall()).getOrElse(defaultValue)
6) String interpolation in scala seems to be exactly the same as kotlin:
val name = "John"
val lastName = "Smith"
val text = "My name is: $name $lastName"
val otherText = "My name is: ${name.substring(2)}"
In addition, you can also have:
val longString = s"""
|This is line 1
|This is line 2, my name is $name
""".stripMargin
7) This shows the same multi-line strings as above, but its unclear if you can have variables in multi-line strings like I showed above in Scala
8) Ternary operator:
val text = if (x > 5) "x > 5" else "x <= 5"
Some bonus stuff which scala has:
9) Case classes:
case class Person(name: String, age: Int)
10) Pattern matching
person match {
case Person("bob", 13) =>
println("Matched bob with age 13")
case Person("sally", _) =>
println("Matched sally, age unknown")
case Person(name,age) =>
println(s"Matched unknown person with name $name and age $age")
case x:_>
println(s"Matched unknown item: $x")
}
10) Equality which actually makes sense, i.e
val foo = "foo"
val bar = "foo"
foo == bar //true in scala, no need to foo.equals(bar)
val bob = Person("bob", 13)
val other = Person("bob", 13)
bob == other //true, based on values
val list = Seq(bob)
list.contains(other) //true, based on values
11) .par collections / way better higher order functions
val list = Seq(string1, string2,...)
//Do a parallelized search on list to find all strings <= 3 chars in length:
val shortStrings = list.par.filter(_.length <= 3).seq
12) Try:
val riskyOp = Try( doSomethingRisky() )
riskyOp match {
case Success(result) =>
println(s"Op succeeded with result $result")
case Failure(e) =>
e.printStackTrace
}
Disclaimer: I wrote all these in the HN textbox, please excuse any typos
Could you please also provide Scala versions for the other code snippets on that site? Just what you can come up with from the top of your head?
I'm genuinely interested, having spent the last few years with Haskell and having recently rediscovered and being incredibly awed by the JVM and its ecosystem, and pondering on whether I should refresh what I'd known of Java or just outright embrace Scala.
You should definitely go for Scala if you're coming from Erlang. You'll find it much more Erlang-like.
I don't have the time to convert their whole site right now, sorry, but you'll find the same pattern holds - Scala does everything more elegantly and concisely. Feel free to look up a Scala tutorial.
Great, thanks! Do you think Martin's textbook on Scala is recent enough relative to the present state of Scala as a language? I also see Scala 3.0 is in talks, nice.
I love programming languages that are case sensitive. So easy for each programmer to have their own programming style on naming variables and then errors galore, which in the end will result in hiring an outside senior to clean-up the mess - that's me of course, and all for heavy money.
Somebody please invent another case sensitive programming language, we don't have enough of them already.
It's hard to conceive of an issue which is less important in modern programming. In 20 years of commercial programming on a variety of platforms my experience is that case sensitivity causes no problems at all and case insensitivity causes only minor and occasional problems.
I would love to live in your bubble. Unfortunately, or perhaps fortunately, depends on what point of view you refer to, as freelancer in past 10+ years, real life is quite different.
Discounting someone else’s experience as “not real” and substituting in your own doesn’t make their viewpoint invalid. And to throw in my own, I have never seen case sensitivity be an actual issue that takes more than a couple minutes to resolve.
Worked with a few case smashers in the 80s. Smashing Unicode is hard, I guess. I don’t really have much of a preference for or against case sensitivity, though I do hate fuglyCaps
Java 14 is getting an experimental preview of Records (https://openjdk.java.net/jeps/359) which takes care of much (but not all) of ceremony around "data classes".
And Java 13 got a preview of text blocks, https://openjdk.java.net/jeps/355.
Things are coming. That said, I hardly believe those minor syntax improvements are what is so "good" about Kotlin. It has some better defaults (non-null by default, final classes by default), and is a more modern language. Java will stay with us for many years to come, though.