8 ms·
I'm currently working on a project which has both Java and Kotlin and I really have to say that I like working on the Java parts much better than on the Kotlin
by dr_faustus 4y ago
I'm currently working on a project which has both Java and Kotlin and I really have to say that I like working on the Java parts much better than on the Kotlin parts. I find coroutines a really strange and leaky abstraction (and the prevalence of suspend methods in libraries which you have to wrap all the time just to call them synchronously is really annoying).
With streams and the var keyword, Java is also not that much more verbose anymore than Kotlin and the code completion, error detection and refactoring capabilities are much better in Java. In Java I can also be sure that if I directly access a property on a class, there will be no side effects or expensive operation while if I use a getter, there might (and I dont have to write them either due to code generation or Lombok). So the parts where Java is still more verbose actually add clarity (and again, due to the IDE, you rarely have to type any of it): I dont really get why I would leave out the explicit types in the source code, only to be added again as non-editable, weirdly positioned text by the IDE (which is not there if just wanna have a quick look at the code on GitHub).
- krzyk 4y agoAnd with records you mostly don't need Lombok (unless you really like builders and can't wait for withers in Java).
- bdangubic 4y agoRecords not being extend(able) is the reason why Lombok is still mostly needed...
- kaba0 4y agoWhy would you need extend over implements? I found sealed interfaces with records really cool.
- vbezhenar 4y agoJPA requires mutable classes. Records ergonomics is terrible if you want to replace data classes with records, even if immutable is OK. You need to write builders. You can't really write code like new Person(id, null, null, null, lastName, firstName, null, null, null, null, null, null, null, age); Withers can help with that, but they're not even in any kind of experimental shape right now, so we have many years to wait for it. Right now records are only fine for very simple use-cases like Point(x, y). Or if you want to write or generate builders. But at this time you don't really save anything, if you can generate a builder, you can generate a class as well.
- krzyk 4y agoYeah, I don't like JPA because of lack of support of immutable types (records or my own classes that don't have setters, only constructor). If you have data class with more than 5 fields then you have it wrong. Builders are like lombok and like field injection: hide poor class design. If using a class hurts, it is badly designed and should hurt until it is refactored/split up into consumable parts. I use records extensively, but those are in most cases converted classes which were small and immutable. JPA being one of the exceptions (a stuck in the past spec)
- kaba0 4y agoWhy not have it like new Person(name, new Address(…))? Nesting is allowed.
- mariusmg 4y ago>I dont really get why I would leave out the explicit types in the source code One my pet peeves, shit like this sucks : var f = MyStupidMethod();
- dalyons 4y agoWhy? The compiler knows what the type is, the ide can tell you the type if you need to know - it’s redundant for you to have to type it out.
- ivanche 4y agoNow do the code review in GitHub. Ooops!
- wiseowise 4y agoI do code review on GitLab all the time, what’s the issue?
- ivanche 4y agoNo compiler there so the compiler can't tell you the type. No IDE there so the IDE can't tell you the type. You're at the mercy of colleague(s) not to write code like var request = service.call() or, worse, var b = getResult().
- RhodesianHunter 4y agoNever have this issue. I'm not being hyperbolic either. I review Kotlin PRs every day and am never lost for context on a type.
- Iwan-Zotow 4y agononsense this line has zero readability you're not writing code for compilers, you're writing code for people
- 4y ago
- mcv 4y ago> With streams and the var keyword, Java is also not that much more verbose anymore than Kotlin I have no experience with Kotlin yet, but I recently did some Java after years of js/ts, and was nearly driven mad by constantly having to convert different kinds of collection. String[], Array, List, Iterable, Stream... and every library expects or returns something else, so you're constantly converting these things. Any of them would have been perfect if only it had been supported by everything, but because of Java's long history of constantly inventing newer and better ways of doing things, libraries written at different times support different kinds of collections and you constantly have to convert them.
- dr_faustus 4y agoI agree to some extent, however, IDEA often helps you with that (e.g. by converting arrays with Arrays.asList(array)). And I have to disagree: Any of them would not have been perfect. An array is sometimes significantly more performant and requires less memory than a list. In the different collections, you see a history of 20 years of developer experience improvements coupled with an almost religious emphasis on backwards compatibility in the language and standard library. In every (long term) JS/TS project I worked on, about 20% - 50% of the development time went into upgrading libraries and keeping everything running on the current node version. It was maddening. Stuff like the different types of collections are a very small price to pay, IMHO, if you can be sure that your current Java project will with very high probability also run on a current JVM 10 years from now.
- mcv 4y ago> Any of them would not have been perfect. An array is sometimes significantly more performant and requires less memory than a list. That's actually why I prefer List, because a List is an interface and can be an ArrayList or another type of List and I don't care about the implementation, as long as it's a List. But Array is an array that's not a List, and Iterable is also an interface, and I think a List is also an Iterable, but I'm not sure, and I suspect not every Iterable is a List. And then there's Stream which should have been a set of convenient functions that are part of List or Iterable right from the start, but they're not. And what the hell is a Spliterator and why do I need one to turn an Iterator into a Stream? I understand that there are reasons for this, and I totally agree that the js/ts dependency situation is far from ideal, but it's still maddening to have so many different collection types, when js/ts just has an array that's not even an actual array, but it still works fine, and all the new stuff just gets added on top of it. One of these days someone should invent a new programming language that finally gets all of this right once and for all, but I bet people will find ways to improve it again.
- foobarian 4y agoI don't find coroutines that helpful, but the syntax sugar and standard library in Kotlin is pretty awesome. The Elvis operator alone is worth the switch. Say you have some container with 3 levels of nesting. In Java, you have to null-check every level vs. 3 "?." dereferences. In theory this is possible to approach with annotations but I found it hard to keep up in a large codebase.
- gosukiwi 4y agoIf you need 3 levels of null-checks that's a red flag though
- deleted 4y ago[deleted]
- afavour 4y ago> I find coroutines a really strange and leaky abstraction (and the prevalence of suspend methods in libraries which you have to wrap all the time just to call them synchronously is really annoying). I think the point is to be calling them asynchronously. Unfortunately async programming is kind of contagious and difficult to shoehorn into sync stuff but if it’s written asynchronously there’s usually a reason.
- bottled_poe 4y agoIs it? Seems to me that most instructions are naturally async or can be refactored as such. I’m probably wrong, just a feeling.
- anthlax 4y agoSure, but the one thing Kotlin has that makes me never look back is null-safety. Java has optional and non-null annotations and whatnot, but you have to be vigilant and put the right annotations everywhere. Kotlin you get this for free. The extra sugar on top is super nice too - listOf, mapOf, x to y, apply… I think it just makes code that much cleaner. None of these are make or breaks but together they work amazing. A lot of what I work on professionally is still on Java 8 or 11 so I don’t get any of the cool features like record types. Most of the OSS I do is Kotlin bedsheets I just enjoy writing Kotlin. I don’t feel that way with the older versions of Java (I’m starting to with the newer ones).
- treis 4y agoHow does this work in a SOA situation? Like do you have null checks + default values at every HTTP boundary for your application?
- anthlax 4y agoMost frameworks give 400 bad inputs. If the client gives a null but null is not allowed in the type, it will auto respond with 400 before it ever invoked the handler. Java spring does the same thing: if you use an unboxed type (int instead of Integer) and the client passes in null, the handler will never be invoked.
- taeric 4y agoBut if you have an evolving API, you are back to what was asked. Either you are a bit of a jerk to all of your existing clients, or you have default values at the boundary. I've found myself with this a lot on "progress" data classes where I will be building up a set of data over several expensive calls. I /could/ make a new type per current state of that, such that I never have nulls. I /could/ make it so that every field is Optional. Or, I could work on a convention that fields are filled out in order, and after the first null, nothing is available. In many years, this has not been where null pointers hit me.
- 4y ago
- kotlin2 4y ago> I find coroutines a really strange and leaky abstraction. I've only used Kotlin on Android, but from what I remember co-routines are not necessary to use. If you don't like them, then don't use them. Kotlin has a number of advantages over Java. The biggest of which is built-in optional typing. It's also really nice that everything is expression instead of a statement. Library functions like `let` also make code a little nicer to write. And stuff like data classes and better property initialization are icing on the cake. In my opinion, you can write Kotlin exactly as you would Java, but the development experience is much more polished.
- origin_path 4y agoWell, the new Android UI framework (Compose) is fully based on Kotlin and coroutines.
- sorokod 4y ago> built-in optional typing Typing is not optional, everything in Kotlin is typed. What the language has is type inference. The following lines have the exactly same types: val teens = people.filter { it.age < 20 }.map { it.name } val teens: List<String> = people.filter { p: Person -> p.age < 20 }.map { p: Person -> p.name }
- kaba0 4y agoJust for comparison’s sake, java is not significantly more verbose. final var teens = people.stream().filter(p -> p.age < 20).map(People::name).toList()
- Matthias247 4y agoI think coroutines is probably the best implementation of cooperative multitasking (async/await) that is is available in any language: They build on structured concurrency and thereby allow to prevent a few common concurrency problems like runaway tasks. You can run them on different threading systems, like UI threads, threadpools, etc. And they default to run the continuation on the correct thread. However as any async/await implementation it will obviously still have challenges. The "colored" world of functions means users need to know the differences between suspend functions and other functions, and using something wrong can lead to performance issues. One thing to note is that with the upcoming of virtual threads (Project Loom) in Java itself, the need for using coroutines might become much smaller. You might just end up using them for UI work, and everything else could use virtual threads. That could simplify a lot of things.
- jillesvangurp 4y agoLoom will just slot into co-routines without requiring many (if any) code changes. It's still going to be nicer to use co-routines from an API point of view and they'll just use the loom stuff underneath pretty much transparently when it is available. A virtual thread is basically just yet another thing that you can wrap with a co-routine. There are many such things across the jvm, js, and ios-native platforms that are supported via Kotlin already. And since it is API compatible with things that are already supported by co-routines, pretty much all you need to do is change your custom co-routine contexts to be backed by virtual threads rather than actual threads. This should just work even without explicit support for this in the co-routines library (it's just another thread pool). But they obviously will add explicit Loom support as well as it makes sense to make co-routines be virtual threads pretty much always. When that ships, all current kotlin code that uses co-routines will be using virtual thread on a Loom capable jvm. It already has structured concurrency and all the rest so all of that will continue to work. And some of the blocking Java stuff that you currently need thread backed co-routine contexts for, will stop being blocking so you can stop doing that. But it will still work of course.
- Matthias247 4y agoI guess you could "slot loom into co-routines", but the niceness of loom is that you don't even have to use coroutines. It could just be regular "blocking" Kotlin functions which run on virtual threads. If you already use Kotlin coroutines - fine to continue to use them. But I imagine for a new project you might want to opt to build on virtual threads instead of coroutines.
- jillesvangurp 4y agoThis sounds like you are trying to shoehorn asynchronous kotlin code into synchronous Java code. Which would be doing it wrong. The problem is trying to make asynchronous code synchronous: don't do that. The whole point of asynchronous is to do it end to end and isolate the remaining synchronous bits and pieces that you have on separate threads so they don't end up blocking all the asynchronous bits and pieces that you have. Use a proper asynchronous framework and then the only thing you need to worry about is avoiding calling synchronous code on your main thread (i.e. dispatch it to some thread pool backed co-routine context). Not that hard. And you can get rid of a lot of that stuff by gradually switching to non blocking versions of whatever you are using. If you are exposing class properties without accessors in Java, that's not necessarily a great thing. Especially if your state is non final (i.e. mutable). In Kotlin, you'd use vals (or vars if you really have to) and by default they just behave like they have getters/setters. But you don't have to spell it out. And you can override the getters and setters. Just like you would in Java. It's just the distinction is not there. It's basically a less hacky Lombok. The weirdly positioned text that your IDE adds are called hints and you can turn them on or off as you please. They are there to help you. Type inference is a nice thing: it means you can read the hints without having to spell out what is what. Java has type inference too but it's a bit more limited and more verbose. There is no uncertainty about what the type of anything is in either language: exactly what you specified (but just once in Kotlin's case).
- sorokod 4y ago> With streams and the var keyword, Java is also not that much more verbose anymore than Kotlin In general Kotlin is more concise, checkout these comparative examples: https://stackoverflow.com/questions/34642254/what-java-8-stream-collect-equivalents-are-available-in-the-standard-kotlin-libr/34661483#34661483 https://stackoverflow.com/questions/34642254/what-java-8-str...
- lmm 4y agoThose examples seem very biased; often the Java version does not use a more concise method that's available, or bloats the code by not importing a static method.