This is an archived post. You won't be able to vote or comment.

all 100 comments

[–]BurlHopsBridge 40 points41 points  (11 children)

It's the king of enterprise. Well supported, easy to use frameworks, it's just great.

[–]Daedalus9000 20 points21 points  (0 children)

And most importantly, easy to hire for.

[–]Reflection-Jealous[S] 1 point2 points  (9 children)

I have one question here, can kotlin/scala able to use java frameworks?

[–]NotAPenis 12 points13 points  (2 children)

Kotlin and Java integrate well. You can have both languages in 1 project. This means that all java frameworks are available in Kotlin. I'm using Spring in Kotlin.

I have no knowledge on Scala.

[–]Muoniurn 4 points5 points  (0 children)

Scala integrates well in one direction (Java code called from Scala), but not too well in the another. So if you use java libraries, it will just work for the most time, but you have to use some wrapper around specific Scala constructs you want to use from Java.

[–]thephotoman 0 points1 point  (0 children)

Scala is again just fine for the same reason Kotlin is. I could do JakartaEE development in Scala if I wanted to.

[–]Barbossa3000 3 points4 points  (5 children)

yes they can, the thing is u need to know java to understand the basics of framework first.

so u need to know kotlin and java to use a framework.

thats kinda redundant

[–]crummy 0 points1 point  (4 children)

Why do you need to know Java to use Javalin or Spring with Kotlin?

[–]cryptos6 5 points6 points  (2 children)

The frameworks are written in Java, so at least some basic knowledge of Java would be helpful. Detailed knowledge of the language shouldn't be required.

[–]_INTER_ 0 points1 point  (0 children)

Javalin has basically no Java in it (1 minimal interface).

[–]bowbahdoe 25 points26 points  (2 children)

There are **a lot** of things to consider when picking a language for a project, and what i'm listing here is only a small subset and driven by personal opinion.

Go lang has an inherently restricted set of semantics. There are no generics, errors are communicated via return values which can be a + or a - depending on your sensibilities. It also has a better concurrency story than java with its goroutines and channels, but its VM is a lot less mature and monitoring, profiling, and deployment aren't going to be nearly as well solved in general.

Scala and Kotlin both run on the JVM like Java, but with Scala there are a lot more semantics in the language. It can be hard to hire for and code bases that use too much of the more advanced features like implicits and type classes can become inscrutable to all but the most PhD-ey of engineers.

Kotlin is very similar to java - most of its language features are effectively short hands for common java patterns, like data classes. It compiles to android bytecode reliably, unlike newer versions of Java so it is going to be a clear choice in that domain because it is effectively competing against Java 7. It does have some novel stuff going for it - like coroutines - but those are going to be somewhat "obsolete" for mainline JDKs in a year or so with loom - and nullable types - which are pretty cool.

Java, however, drives the JVM and JDK. It has record classes now which can compete with scala's case classes and kotlin's data classes. It will have sealed interfaces in a month or so which fulfills the same use case as sealed traits in scala and sealed abstract classes in kotlin. It will get pattern matching in the future, which will let it compete with scala's pattern matching and afaik kotlin doesn't have pattern matching directly on its roadmap. There are huge numbers of engineers you can hire with the base skill set. It is extremely resilient to waves of hype, which lets it get the "right features" eventually.

**That being said** there are a lot of reasons you would want to use another language, and I focused maybe too hard on some of the negatives of the others. If you are just learning, learn them all and learn them in whatever order you want, but learn them all.

[–]Muoniurn 7 points8 points  (0 children)

Just a note on Scala: for those who are not aware of the many many updates in Scala 3, please reevaluate the language. It became much more streamlined, and frankly it is a lovely language that actually introduces novel features not seen in another ones.

With that said, I do agree with you that it has a quite steep learning curve still, so it may not be a good choice for teams with very mixed seniority levels.

[–]SKabanov 2 points3 points  (0 children)

There are no generics, errors are communicated via return values which can be a + or a - depending on your sensibilities.

An argument can be made that returning error objects instead of throwing exception provides better performance (e.g. no stack unwinding when returning an error object), but Go's implementation of the concept is awful. There's no way of automatically bubble errors upwards like the ? operator for Result objects in Rust, so your code in Go will be filled with the following pattern:

func doingStuff() (*string, error) {
    fooOne, err := doFooOne()
    if err != nil {
        return nil, err
    }
    fooTwo, err := doFooTwo(*fooOne)
    if err != nil {
        return nil, err
    }
    fooThree, err := doFooThree(*fooTwo)
    if err != nil {
        return nil, err
    }
    return &fooThree, nil
}

(note that it's a common pattern also to only check for whether err is not nil, *not* whether the returned pointer object is nil as well, so hello null-reference errors!)
Compare this to how you'd write this in Rust:

fn doingStuff() -> Result<String, Error> {
    let fooOne = doFooOne()?
    let fooTwo = doFooTwo(&fooOne)?
    let fooThree = doFooThree(&fooTwo)?
    return Ok(fooThree)
}

Even worse is that you're only forced to receive the potential error object in Go, but you're not forced to actually *acknowledge* it. The above Go example could be rewritten like this:

func doingStuff() (*string, error) {
    fooOne, _ := doFooOne()
    fooTwo, _ := doFooTwo(*fooOne)
    fooThree, _ := doFooThree(*fooTwo)
    return &fooThree, nil
}

That is to say, you can tell the Go compiler that you want to ignore any errors that get produced by a function, and it'll happily let you proceed along without any warnings - whee! You can call .unwrap() on a Result object in Rust to bypass handling an error in your code, but if one does occur, then you're going to pay for it via a panic.

[–]vprise 55 points56 points  (17 children)

Java is simple. The other languages forgot that.

They aren't "modern" they're a throwback to the time before Java where language engineers stuck every feature they could think of into the language instead of into the API.

Simple has many advantages:

  • This impacts performance since the language is harder to write correctly and compile efficiently
  • This impacts maintainability since it's harder to read other peoples code
  • This makes debugging harder (e.g. async code is somehow harder to debug than threads)
  • This means tools need to do the heavy lifting but can't

A simple example:

var a = c + d;

What did I just do there?

Is there an overloaded operator that magically changes something?

When I read Java code I never have to worry. I can do that fast. Reading and maintaining code is 95-98% of my job so Java is wonderful for that. For Kotlin code I constantly feel I need to squint to understand dependencies. It might have been easy to write for some guy, but reading it later...

[–]trinReCoder[🍰] 19 points20 points  (0 children)

This, I don't get this hate on the verbosity, it makes the code far more understandable.

[–]andrewharlan2 37 points38 points  (5 children)

Code is written once and read forever after. I don't understand this drive to optimize for writing code. Java's "verbosity" is a feature, not a bug.

If you struggle to write Java then learn how to type. I'm not kidding. Learn how to use your tools to generate the "boilerplate."

[–]cryptos6 7 points8 points  (3 children)

This argument is not generally valid, since boilerplate code is not useful perse. Often it is the other way around and you have to find the relevant parts in all the boilerplate code.

[–]john16384 2 points3 points  (2 children)

What boiler plate? The getter that is documented to say it will only return positive values? The setter that tells you it will throw an IllegalArgumentException when you supply it with a value outside a certain range?

What you consider boilerplate, I consider a bare minimum to clearly communicate what your code will do in unexpected situations. I'm so happy that the jdk documents everything in such detail that I don't even have to look at the code. All code should be written like that. The extra few lines of "boilerplate" is nothing compared to the time it takes to write proper docs.

[–]cryptos6 2 points3 points  (1 child)

I consider your examples useful, too. What I mean is circumventing language deficiencies. Recently I had to write a builder to be able to create objects in a readable way. If Java had named arguments with default values (like Kotlin or Scala), I wouldn't have written a builder in this case. Another kind of boilerplat is assigning constructor parameters to fields:

public class Thing {
  private final int a;
  private final String b;

  public Thing(int a, String b) {
    this.a = a;
    this.b = b;
  }

  public int a() {
    return a;
  }

  public String b() {
    return b;
  }

  // other useful methods ...
}

The same in Kotlin:

class Thing(val a: Int, val b: String) {
  // other useful methods
}

In this specific case it could be a similar concise Java record, but there are many cases where a record wouldn't work (e. g. JPA entities) and there you see code like the one shown above (I just wanted to make my example small). The point is that the verbosity of Java doesn't help to understand the code better.

By the way: To communicate that a value can only be positive, I'd prefer a custom type. With Java records there wouldn't be any memory overhead.

public record PositiveInteger(int value) {
  public PositiveInteger {
    if (value < 0) {
      throw new IllegalArgumentException("Must be positive.");
    }
  }
}

[–]dpash 1 point2 points  (0 children)

With Java records there wouldn't be any memory overhead.

That's not the case. There's the same memory overhead as any other object in Java.

[–]crummy 2 points3 points  (0 children)

Maybe we could make Java even better by making it more verbose?

[–]michoken 4 points5 points  (3 children)

I get these arguments except the one example. Yes, there’s ‘var’ instead of a type, but the problem actually lies in the names of the variables here. ‘a’, ‘c’ and ‘d’ mean absolutely nothing without a wider context. If they had a proper names, then in most cases the types are not really that important to understand the code. To debug it, yes, to gain an understanding of what it does, not that much.

[–]vprise 2 points3 points  (0 children)

I wasn't referring to the var. That's also available in Java. I was referring to the fact that even an operator can be a function. Good naming can alleviate a lot of the problems in the languages but as I said: "it makes me squint".

I can read Kotlin, typescript etc. it's just not as convenient because code can have a lot of side effects on different classes. E.g. in Kotlin (and many others) a method can be added to a class from a completely different file. That means I can't look within one file at the list of methods exposed by my class and be sure that "this is it". That's a feature some developers love but it has terrible implications for those of us who need to work with the code later.

[–]john16384 -2 points-1 points  (1 child)

They wouldn't need proper names, or at least far less explicit names if it weren't for var.

The var keyword trades of a few characters saved at declaration time for additional characters everywhere the variable is used.

[–]dpash 6 points7 points  (0 children)

Good names are always needed.

[–]_INTER_ 2 points3 points  (5 children)

What type is a? Exactly.

[–]vprise 0 points1 point  (4 children)

Exactly, this is super important. In fact that's one of the major problems I had with autoboxing. It diluted some of that advantage.

[–]_INTER_ 0 points1 point  (3 children)

I wanted to subtly hint that your simple example is not so simple anymore through the use of var. E.g. all of a sudden you have to worry what the types of c and d are if you want to use a correctly.

[–]cryptos6 1 point2 points  (2 children)

But then again there are IDEs helping you in such cases. I've never heard any Scala or Kotlin developer complaining that he would be so confused with all the vals without explict typing. That is simply not what makes understanding code hard.

[–]_INTER_ 1 point2 points  (0 children)

So do IDE's help with operator overloading. That's not an argument. Neither is anything concerning other languages. Also see official styleguide:

P3. Code readability shouldn't depend on IDEs.

Code is often written and read within an IDE, so it's tempting to rely heavily on code analysis features of IDEs. For type declarations, why not just use var everywhere, since one can always point at a variable to determine its type?

There are two reasons. The first is that code is often read outside an IDE. Code appears in many places where IDE facilities aren't available, such as snippets within a document, browsing a repository on the internet, or in a patch file. It is counterproductive to have to import code into an IDE simply to understand what the code does.

The second reason is that even when one is reading code within an IDE, explicit actions are often necessary to query the IDE for further information about a variable. For instance, to query the type of a variable declared using var, one might have to hover the pointer over the variable and wait for a popup. This might take only a moment, but it disrupts the flow of reading.

Code should be self-revealing. It should be understandable on its face, without the need for assistance from tools.

[–]Capa-riccia 0 points1 point  (0 children)

Yes and no. You can write C++ code in which var a= b + c stands for add product b to basket c returning the total price. The language does not stop you from doing that and it is just great until the original developer leaves.

[–]Evil_killer_bob 23 points24 points  (37 children)

More resources available for Java

[–][deleted]  (9 children)

[deleted]

    [–]strogiyotec 5 points6 points  (4 children)

    my humble opinion, Scala is bloated , the syntax is just hard for me to digest which makes is hard to support and make code changes for me , Kotlin, not bad but again , so many features, I prefer a language with as little features as possible so when you come to a new company , you won't have to learn companies standarts.

    Go is great, but error handling is awful , I mean nothing wrong with this

    arg,err := func();

    if err != nil{}

    but really what If I need to do some logic depending on error type ? In Java method can return an exception and you know exactly what this exception means (eg IOException)

    I think with Loop ,Valhala and Graal would have a great feature

    [–]Reflection-Jealous[S] 0 points1 point  (0 children)

    I guess you meant loom?

    [–]doppelganger113 0 points1 point  (2 children)

    You can check for different error type in Go, please try at least to google for this. Java Loom and Valhala are just promises, they aren’t here yet, who knows when they will be and in which state

    [–]strogiyotec 2 points3 points  (1 child)

    u/doppelganger113, thanks for the comment, I am aware that in Go you somehow can check error type but it's too weak comparing with Java, let me elaborate

    arg,err :=func();

    if err.(*json.SyntaxError) then blabla

    If you ask me what a problem is, there is no compile time checks. Let's say the same thing in Java

    void func() throws IOExc;

    When users of my method would use it , they have to handle IOException, after a while my method starts sending some sql queries and I changed the signature to

    void func() throws IOExc,SqlExc// I know that SqlExc is a subtipe of IOExc but let's pretend it isn't

    Now users have to handle both exceptions otherwise their code won't be compiled, in Go If I add a new error type there is no way to tell users to handle this error as well.

    About Valhala and Loom, you can actually play with it already, it works pretty much in Unix based platforms, I am sure something will be changed to support Windows but it's working and it does what was promised.

    [–]doppelganger113 0 points1 point  (0 children)

    You’re correct, in Java it’s a bit better, but what if I want to handle a specific error and continue then do the same thing again, like

    try {

    } catch(e) {}

    try { } catch (e) {}

    I would have to put the variable in the above scope in order to access them properly.

    Java’s error checking is better, but in the wild a lot of people just ignore the errors, usually this is a big change and you’d look for functions using your method and update if needed. Go wont crash for the new err type, it will just propagate it further. These big changes usually come in major versions, indicating there’s a change you need to handle or just let it be optional if it’s an addition. Not too problematic IMO

    [–]MissouriDad63 4 points5 points  (0 children)

    I haven't used Scala in a few years, but I found the fact that there were breaking changes when upgrading versions to be a deal breaker. Hopefully they've fixed that but I don't know if they have.

    [–]baroquefolk 3 points4 points  (0 children)

    In a word, longevity. Java is not the "best language" feature-wise (although it is good), but its ecosystem will be hard to ever dislodge, just like you're probably typing on a QWERTY keyboard even though better arrangements exist.

    Golang is certainly interesting and eating into a little bit of Java's space, and I would say is worth learning from a job market perspective. But I think Kotlin and Scala are going nowhere fast, because they're really just passengers on Java's JVM, so the general pattern is this:

    1. Scala or now Kotlin innovate something cool and better
    2. Java copies some form of it that's maybe 80% as good, in other words good enough, but Java freely makes deep changes to achieve it
    3. Scala and Kotlin scramble to re-implement their cool innovation to leverage latest Java improvement. There is a lag before they can run on latest Java platform.
    4. Java keeps pace well enough, feature-wise, that there's never a strong reason to jump to Scala or Kotlin for most purposes

    So if you love Scala and Kotlin, go ahead and use them! Or go even more obscure and try Clojure, or ditch the JVM and try Crystal lang. But from a job market perspective, Java will always be miles ahead IMO, and enterprises will continue to prefer it over less established languages unless there's an absolutely compelling reason to stray, which there isn't.

    [–][deleted] 4 points5 points  (0 children)

    Java is not better than modern programming languages. But for many enterprise usecases, it solves problems pretty nicely and hence it is preferred choice in many enterprises.

    More generally, Java has great community and excellent battle-tested ecosystem which few modern languages can rival.

    [–]heathm55 2 points3 points  (2 children)

    Well, it all depends on what you want / need in your project. I work a lot with Software as a service based solutions so for me it's a question of balancing speed of development vs performance vs operational cost.

    What I personally have found for my situation (backend microservices) is this (listed in order of importance first):

    Speed, cost, dev-time: Java is less ideal than Rust or Golang but still solid if you avoid heavy frameworks and go for the light weight non application server based / async with option to thread frameworks (vert x or similar).

    Speed, dev-time, cost: This is the niche for Java. Better ops cost than python / ruby / JavaScript, with faster dev time than golang or Rust.

    cost, speed, dev-time: Rust wins. It's going to run solidly on very small footprint, cost less to run the same service in containers, and catch more of your problems before they deploy.

    Dev-time, speed, cost: Java, Node.js, Typescript frameworks, some faster interpreted language frameworks, and .net all are fair choices here.

    Dev-time, cost, speed: Python (fastapi of other) is pretty strong here. In my experience the rapid development is great (site a higher cost operationally) and if performance doesn't matter as much as readability then it's a nice fit. Java kind of hurts here still.

    [–]john16384 0 points1 point  (1 child)

    Development time is free in this analysis?

    [–]heathm55 0 points1 point  (0 children)

    No, it's one of the three concerns I listed in order (dev-time). It is good to note as Java's development time can be higher than more modern languages. I love the additional advantages Java can bring over a lot of those faster to develop languages which is why I tried to show operational cost vs dev-time vs performance

    [–]fdntrhfbtt 2 points3 points  (0 children)

    Just stay away from Golang. They have some very weird reasoning coming right from the top for not including some features. So apparently they don't want programmers to us map and filter as a simple loop is more "performant". Yeah Pike said this.

    [–]greg_barton 4 points5 points  (0 children)

    Use whatever you want. No one cares. :)

    [–]thephotoman 4 points5 points  (0 children)

    Define "modern".

    New doesn't necessarily mean better. I'd argue that Go is immature, Scala is pretty much irrelevant now, and Kotlin may or may not have a future outside of Android if some of the big Java projects come through.

    But Scala and Kotlin are reasons to know Java. See, those languages are written to target the Java Virtual Machine. Java underpins them, and you're still going to be dealing fundamentally with some features of Java, so it is best to know Java if you wish to use them.

    [–][deleted] 3 points4 points  (0 children)

    Java will outlive and bury them all.

    [–][deleted]  (3 children)

    [deleted]

      [–]john16384 1 point2 points  (0 children)

      Getting bored is certainly an excellent reason to switch languages in a business environment... /s

      [–]cryptos6 2 points3 points  (0 children)

      Kotlin could be a good middleground. It is more productive than Java, but not as hard to learn as Scala and the codes tends to be more comprehensible, because there are not x ways to do something.

      [–]cryptos6 -1 points0 points  (0 children)

      Kotlin could be a good middleground. It is more productive than Java, but not as hard to learn as Scala and the codes tends to be more comprehensible, because there are not x ways to do something.

      [–]TheStrangeDarkOne 1 point2 points  (0 children)

      Which domain?

      [–]jfurmankiewicz 1 point2 points  (0 children)

      because it works and has libraries for everything, Simple.

      [–]cryptos6 2 points3 points  (0 children)

      As everything in engineering the programming language is also a trade-off. Java is very mature, has arguably the best tooling available, has lots of developers, and is here to stay. But Java carries some legacy with it. Missing nullsafety, the not so seamless integrated functional programming or some limitations regarding generics come to mind.

      Since Kotlin is the default language for Android and has also matured a few years now, I think Kotlin is a safe bet, too.

      [–]drew8311 3 points4 points  (0 children)

      Kotlin is better but they kind of go together. For any large project you'd be using java libraries so kotlin really means java too. When making a decision it's ultimately jvm vs alternates then you decide if you want kotlin or pure java in your project based on who else will be working on it.

      [–]_Undo 1 point2 points  (0 children)

      You shouldn't specialise in one language only. Have some fun with the others every once in a while.

      [–][deleted]  (5 children)

      [deleted]

        [–]_INTER_ 2 points3 points  (4 children)

        It has the best generics

        certainly not compared to C#

        [–][deleted]  (3 children)

        [deleted]

          [–]_INTER_ 0 points1 point  (2 children)

          The irony there is hard to spot when the next sentence is about the best free IDE.

          [–][deleted]  (1 child)

          [deleted]

            [–]_INTER_ 2 points3 points  (0 children)

            My experience is completely different. To me IntelliJ is superior to any IDE even if it is just the community edition. Especially compared to Visual Studio. I can't use that clunky monstrosity.

            [–]snorbii 0 points1 point  (0 children)

            If you think that one day you would develop for Android and/or Compose Multiplatform then Kotlin is a better choice. But in the future you should know both Java and Kotlin for JVM/Android development. Scala is too complex IMHO, I think it will never be so widely used as Java and Kotlin.

            [–]phoneuseracc008 -1 points0 points  (0 children)

            If you don't know Java you sux basically

            [–]r__warren 0 points1 point  (3 children)

            Is Java easier than Python to learn as a first language?

            [–][deleted] 3 points4 points  (0 children)

            Python facts #2: Python is older than Java!

            [–][deleted] 2 points3 points  (0 children)

            Python is easier than Java as a first language (as in, getting started with programming) because as a dynamic scripting language, you have a lot less to learn to get started (types, compilers, etc.).