I recently spent some time “broadening my language pool”. Some are out of utilitarian perspective (Java Web development, Scala big data framework), while others are out of pure interest (Groovy, Go), and have learned different programming ideas and styles in the process of learning different languages. However, I have to learn too many things at the same time recently, resulting in a bit of confusion in the process of constantly switching languages. This article summarizes some similarities and differences of language features of Java, Scala, Groovy and Go that I have learned, so that I can quickly recall how to manipulate them in the future when necessary.

If I want to learn a new language quickly, I will start from the following angles: data types, function definitions, dependency imports. Data structures are always generic: arrays, lists, maps, and some common operations in daily development, such as HTTP, database connections, sockets, and IO streams. In my opinion, with this knowledge, I am able to gradually get started with some development tasks (except for those that dig deep into language features). All that remains to be done is to solve the requirements through the appropriate development framework.

1. Outline

Java: The syntax is relatively fixed, there is no need to worry about the syntax when developing, and the communication cost and development cost are very low when developing the team. The largest audience, any questions can be found on the Internet.

Scala: Lots of implicit conversions, native support for currification, higher-order typing, type-typing, very flexible operator overloading, and the ubiquitous _ symbol (which takes Scala programmers to the level of “stream of consciousness programming”). The unfettered syntax results in Scala code styles that vary from developer to developer and are not well suited to large team collaborations. In addition, the IDE’s support for the language itself is mediocre (making do by installing plug-ins), plagued by complex compilation mechanisms and sometimes obscure error prompts.

Groovy: Dynamic type determination, metaprogramming, method runtime injection, and a lot of “icing” for the JDK to do some jVM-based automated maintenance. However, high sweetness and high dynamics come at the expense of performance, and like Scala, there is no “handy” IDE (again, by installing plug-ins), and occasional bugs from the IDE.

Go: Provides communication programming based on CSP theory, Goroutine & Chan, is naturally capable of asynchronous programming. Short variable naming is supported and unused library references or variables are not allowed. It has Pointers, but it also has GC; Cross-platform is definitely not as good as JVM, at least take the source code to other platforms and rebuild go. There is a dedicated IDE, GoLand, that makes writing code very comfortable.

2. Programming paradigm

Java: OOP is still the dominant, FP experience is mediocre (or poor) because Java forces me to memorize the names of various functional interfaces.

Scala, Groovy: Supports both FP and OOP programming styles. Scala has a very straightforward notation for function types, while Groovy treats all closures as Closure

.

Go: Function is everything. Go abandons the inherent OOP pattern, so there is no notion of inheritance, polymorphism (but that doesn’t mean Go isn’t OOP). Go encourages code reuse in the form of composition.

3. About static

Of the four languages, only Java and Groovy have the “static” setting.

Scala considers this to be against OOP, so the concept of “static” is replaced by the companion object; Go believes that “static” content can be replaced by pure functions or package variables, so it has no concept of static.

4. Access permission control

Java: public, protected (package and subclass visible), default (only this package visible), private.

Scala: Public by default, protected (visible to subclasses only), private. Additional package variables are provided to enable members of a package to share data.

Groovy: Public by default, followed by protected (package and subclass visibility) and private. In fact, the compiler can’t do anything about cross-permission access (just a warning), because Groovy has plenty of ways to access it “legally.”

Go: Does not need any keywords, only the case of the first letter of variables/functions determines whether they can be exported.

5. Division of data structure

Groovy basically follows the Java tradition of eight basic data types: byte, short, int, Long, float, double, char, Boolean.

Scala encapsulates Java’s “old eight”, with toXXX methods for casting each type; Has a top type Any and a bottom type Nothing; Scala is very concerned about whether a variable should be an immutable val or a mutable var.

Go divides data into int8 ~ INT64 (including unsigned uint8 ~ uint64), float32, float64. There is a special type: unsigned type, which is used to maintain high precision of constants.

6. Processing of characters and strings

A Java char represents a “character” that is internally utF-16 encoded for the Unicode character set, fixed to two bytes (a very small number of characters may require two char stores). When data is serialized externally, utF-8 encoding is used. In this case, the length of a character ranges from 1 to 3 bytes. JVM strings are implemented by the java.lang.String class, which has various String manipulation methods built in.

On top of that, Groovy wraps all characters, strings, as strings or gStrings.

Go language has the native string type, and the encoding method is utF-8. However, if you intercept a single element of string, you can only get byte. Individual characters in Go are represented using rune, and some operations involving characters first cast string to []rune (in this way, Go’s []rune is more like a Java string). For example, “counting words” calls utf8.runecount (). String processing generally relies on strings, Strconv, and Unicode packages.

7. Types and type derivation

Java: Starting in JDK 11, you can use var to force the compiler to do type inference for local variables. Support for generics, generic upper and lower bounds, but no generic variant (arrays support covariant). Higher-order types are not supported.

Groovy: It’s not so much type derivation as deferring type validation to runtime.

Scala: Native support for type derivation, support for generics, support for the concept of type variation, in addition to context and view definition. Support for higher-order types, which can be obtained by reflection.

Go: Natively supports type inference and currently does not support generics (nor should it ever).

8. Define interfaces

Java: after JDK 8, support to set default methods, static methods, interface constants; Since JDK 9, setting private methods has been supported.

Groovy: Allows you to dynamically combine multiple closures into a Map and declare it as an implementation of an interface.

Scala: Allows you to dynamically mix and overlay multiple attributes when creating an object, as well as its own attributes that specifically serve a class.

Go: You don’t have to declare that the interface is implemented, as long as a type struct implements all the methods of the interface, it can be used as the interface (my favorite way).

9. On function definitions

Java: Mediocre.

Scala: Native support for cremation, which is divided into pass-through calls and pass-through calls.

Groovy: Functions are called closures, and all the other FP features are available.

Go: Supports returning multiple values, supports defer calls, and makes it easy to shut down resources using the Execute Around Method (aOP-like) pattern.

10. Loop structure

Groovy: The Java syntax also works in Groovy, with additional support for(… in ..) {} implements traversal (as Python does).

Scala: For expressions have a high entropy of information and can express map, flatMap, foreach, withFilter, etc. Conversely, the combination of these semantics can be simplified using for expressions. Recursive problem solving is favored, and the compiler can convert tail recursive functions into equivalent iterative code.

Go: only use for expression, can be combined with the range keyword to achieve sequence, map traversal.

11. Exception handling

Java: Checked exceptions must be try-catch or thrown to a higher level.

Scala, Groovy: No active handling of checked exceptions in Java is required.

Go: Except for severe problems using panic outages, general exceptions (the error interface) are passed externally as return values.

12. There is about arrays

In Go, the length of an array is part of the array type, with a unique slice type. The other three are much the same.

13. There are about commonly used sets

Java: The List and Map interfaces are commonly used (Set stands for mathematical Set). There are multiple implementations under each interface, which can be based on the actual situation (random access? Or is there a lot of insertion? Is it necessary to record the insertion order when inserting key-value pairs? To choose the right type. They need to switch to Stream when performing map, filter, etc.

Groovy: Uses Java’s ArrayList and LinkedHashMap to transform collections directly.

Scala: Focuses on mutable and immutable types and supports direct (many complex) transformations of collections.

Go: create a map by make(map[key]value), create a list by list.new(), and do not directly provide set transformation operations.