Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What do Groovy, Scala and Clojure in Java have in common?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

What do Groovy, Scala and Clojure have in common in Java? aiming at this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.

The limitations faced by the Java programming language at its birth are different from those faced by today's developers. Specifically, due to the performance and memory limitations of hardware in the mid-1990s, primitive types exist in the Java language. Since then, the Java language has evolved, eliminating many troublesome operations through automatic boxing (autobox), while the next generation languages (Groovy, Scala, and Clojure) go a step further, eliminating inconsistencies and conflicts in each language.

In this installment, I'll show how next-generation languages can eliminate some common Java limitations, both grammatically and in default behavior. The limitation is the existence of primitive data types.

The demise of primitives

The Java language started with eight pairs of primitives and corresponding type wrapper classes (originally used to address performance and memory limitations) and gradually watered down the differences between them through automatic boxing. Java's next-generation language goes a step further, making developers feel as if there is no difference at all.

Groovy completely hides primitive types. For example, int always means that Integer,Groovy automatically handles upconversion of numeric types to prevent numeric overflow errors. For example, look at the Groovy shell interaction in listing 1:

Listing 1. Groovy's automatic handling of primitives

Groovy:000 > 1.class = = > class java.lang.Integer groovy:000 > 1e12.class = > class java.math.BigDecimal

In listing 1, Groovy shell shows that even constants are represented by the underlying class. Because all numbers (and other disguised primitives) are real classes, metaprogramming techniques can be used. These techniques include adding methods to numbers (which are often used to build domain-specific languages, that is, DSL) to support expressions like 3.cm. In a later article on extensibility, I will cover this feature in a more comprehensive way.

As in Groovy, Clojure automatically shields the distinction between primitives and wrappers, allowing method calls to be performed on all types and automatically handling type conversions for capacity. Clojure encapsulates a large number of underlying optimizations, which are detailed in the language documentation (see Resources). In many cases, the type hints can be provided to enable the compiler to generate faster code. For example, instead of using the (defn sum [x]...) definition method, you can add a type hint, such as (defn sum [^ float x]...), which generates more efficient code for the critical section.

Scala also shields the differences between primitives, usually using the underlying primitives for the timeliness parts of the code. It also allows methods to be called on constants, just like in 2.toString. With its ability to mash up primitives and wrappers, for example, Integer,Scala is more transparent than Java autoboxing. For example, the = = operator in Scala works correctly on primitives and object references (comparing values, not references), rather than the Java version of the same operator. Scala also includes an eq method (and a symmetrical ne method) that always compares whether the underlying reference types are equivalent. Basically, Scala intelligently switches the default behavior. In the Java language, = = compares reference data, which you hardly need to do, and you can compare values using the less intuitive equals (). In Scala, = = runs correctly (compare values), and regardless of the underlying implementation, it also provides a way to perform a less common reference equality check (reference equality check).

This feature of Scala shows that one of the important advantages of Java's next-generation language is that developers have more time to think about higher-level issues by offloading low-level details to the language and runtime.

Simplify default behavior

There is a high degree of consensus, and most Java developers agree that operations common in the Java language require too much syntax. For example, attribute definitions and other boilerplate code clutter class definitions, masking important methods. All Java next-generation languages provide a way to simplify the creation and access process.

Classes and case classes in Scala

Scala has simplified class definitions to automatically create access functions, assignments, and constructors for you. For example, look at the Java class in listing 2:

Listing 2. Simple Person class in Java

Class Person {private String name; private int age; Person (String name, int age) {this.name = name; this.age = age;} public String getName () {return name;} public int getAge () {return age;} public void setAge (int age) {this.age = age } @ Override public String toString () {return name + "is" + age + "years old.";}}

The only non-boilerplate code in listing 2 is the rewritten toString () method. The constructor and all methods are generated by IDE. It is more important to understand it easily in the future than to generate code quickly. Useless syntax increases the amount of code you must use before you can understand the underlying meaning.

Scala Person class

Shockingly, the simple three-line definition written in Scala in listing 3 creates an equivalent class:

Listing 3. Equivalent classes in Scala

Class Person (val name: String, var age: Int) {override def toString = name + "is" + age + "years old."}

The Person class in listing 3 is condensed into a mutable age property, an immutable name property, and a constructor with two parameters, as well as my rewritten toString () method. It's easy to see the uniqueness of this class because the interesting parts are not buried in the syntax.

Scala's design emphasizes the ability to create code with a minimum of syntax, making many grammars optional. The simple class in listing 4 demonstrates a Verbose class that changes a string to uppercase letters:

Listing 4. Verbose class

Class UpperVerbose {def upper (strings: String*): Seq [String] = {strings.map ((s:String) = > s.toUpperCase ())}}

Much of the code in listing 4 is optional. Listing 5 shows the same code, now using an object instead of class:

Listing 5. A simpler object that is converted to uppercase

Object Up {def upper (strings: String*) = strings.map (_ .toUpperCase ())}

For Scala code equivalent to Java static methods, you can create an object (a Scala built-in entity equivalent to a singleton instance) instead of a class. The return type of the method, the parentheses used to separate the body of the single-line method, and the useless s parameter in listing 4 all disappear from listing 5. This "collapsible grammar" in Scala has both advantages and disadvantages. With collapsible syntax, you can write code in a way that is very consistent with language habits, but this makes it difficult for unfamiliar people to understand your code.

Case class

Simple classes used as data holders are common in object-oriented systems, especially those that must communicate with different systems. The popularity of this type of class has taken the Scala project a step forward, creating the case class. The case class automatically provides a variety of convenient syntax:

You can create a factory method based on the name of the class. For example, you can construct a new instance without using the new keyword: val bob = Person ("Bob", 42).

All parameters in the parameter list of the class are automatically val, that is, they are maintained as immutable internal fields.

The compiler generates reasonable default equals (), hashCode (), and toString () methods for your class.

The compiler adds a copy () method to the class so that you can return a copy to perform the variant change.

The next generation of Java languages not only fix grammatical flaws, but also promote a more accurate understanding of how modern software works, and the tools that shape them in this direction.

Automatic generation properties of Groovy

In the next generation of Java languages, Groovy is the closest to Java syntax, providing a code generation method called "syntactic-sugar" for common situations. See the simple Groovy Person class in listing 6:

Listing 6. Groovy Person class

Class Person {private name def age def getName () {name} @ Override String toString () {"${name} is ${age} years old."}} def bob = new Person (name: "Bob", age:42) println (bob.name)

In the Groovy code in listing 6, defining a field def results in an access function and an assignment function. If you like only one of these functions, you can define it yourself, just as I did with the name property. Although the method is called getName (), I can still access it through the more intuitive bob.name syntax.

If you want Groovy to automatically generate equals () and hashCode () method pairs for you, you can add @ EqualsAndHashCode comments to the class. This annotation uses Groovy's abstract syntax tree (Abstract Syntax Tree, AST) transformation to generate methods based on your attributes (see Resources). By default, this comment considers only properties (not fields); if an includeFields=true modifier is added, it also considers fields.

Mapping record of Clojure

You can create the same Person classes in Clojure as in other languages, but this is not in line with language conventions. Traditionally, languages such as Clojure rely on mapping (name-value pairs) data structures to hold this type of information and use functions that handle that structure. Although structured data can still be modeled in maps, it is now more common to use records. Records are Clojure's more formal encapsulation of type names with attributes (often nested), and each instance has the same semantic meaning. Records in Clojure are like struct in a C-like language. )

For example, consider the following person definitions:

(def mario {: fname "Mario": age "18"})

Given this structure, the age can be accessed at (get mario: age). Simple access is a common operation on mapping. With Clojure, you can take advantage of syntax candy that uses keys as access functions on maps to use a more efficient (: age mario) shorthand. Clojure expects to operate on mappings, so it provides a lot of syntactic sugar to simplify this operation.

Clojure also has syntax sugar to access nested mapping elements, as shown in listing 7:

Listing 7. Shorthand access to Clojure

(def hal {: fname "hal": age "17": address {: street "Enfield Tennis Academy": city "Boston": state "MA"}}) (println (: fname hal)) (println (: city (: address hal) (println (- > hal: address: city))

In listing 7, I define a nested data structure called hal. Access to external elements takes place as expected ((: fname hal)). As shown in the penultimate line in listing 7, the Lisp syntax performs an "inside and outside" evaluation. First, you must get the address record from hal, and then access the city field. Because "inside and outside" evaluations are a common usage, Clojure provides a special operator (- > thread operator) to reverse expressions, making them more natural and readable: (- > hal: address: city).

You can create an equivalent structure using records, as shown in listing 8:

Listing 8. Create a structure using records

(defrecord Person [fname lname address]) (defrecord Address [street city state]) (def don (Person. "Don"Gately" (Address. "Ennet House"Boston", "MA")) (println (: fname don)) (println (- > don: address: city))

In listing 8, I created the same structure using defrecord, resulting in a more traditional class structure. With Clojure, you can achieve the same convenient access in the record structure through familiar mapping operations and dialects.

Clojure 1.2 adds grammatical sugar around the record definition of common operations through two factory functions:

-> type name, location parameter of the receive field

-> Mapping-> Type name, keyword mapping of field values

Using functions that conform to language habits, the code is converted from listing 8 to a version of listing 9.

Listing 9. Clojure's beautiful syntax candy

(def don (- > Person "Don"Gately" (- > Address "Ennet House"Boston", "MA")

In many cases, records are more popular than mappings and flat structures. First, defrecord creates a Java class to make it easier to use in multi-method definitions. Defrecord then specifies more tasks, enabling field validation and other nuances when you define the record. Third, recording is much faster, especially if you have a fixed set of known keys.

Clojure uses a combination of records and protocols to construct code. A future article will introduce their relationship.

Concluding remarks

Compared with the Java language, all three Java next-generation languages provide more convenient syntax. Groovy and Scala make it easier to build classes and common situations, while Clojure enables maps, records, and classes to interoperate seamlessly. A common theme of all Java next-generation languages is to eliminate unnecessary boilerplate code. In the next article, I will continue to explore this topic and discuss some exceptions.

This is the answer to the question about what Groovy, Scala and Clojure have in common in Java. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report