In a keynote speech with Martin Fowler, he provided a keen observation report:
The legacy of Java is the
platform , not the
language .
The original Java technical engineer had made a great decision to separate the language from the runtime, eventually enabling more than 200 languages to run on the Java platform . This infrastructure is critical to the long-term viability of the platform because the life of the computer programming language is usually very short. Since 2008, the annual Oracle-hosted JVM Language Summit provides an opportunity for developers of alternative languages on the JVM to work openly with platform engineers.
Welcome to the Java Next Generation column series. Here, I will briefly describe three modern JVM languages: Groovy, Scala, and Clojure, which have an interesting combination of paradigms, design choices, and comfort factors. I'm not going to go into the details of each of these languages here, which are available on their respective websites (see Resources). But the language Community website (which is primarily intended for evangelism) does not provide an example of objective information or tasks that the language does not adapt to. In this series of articles, I will make a substantial comparison to help fill this void. This article prepares an overview of the Java Next generation language and the benefits of learning these languages.
Beyond Java
The Java language is known for Bruce Tate's reputation as a perfect storm in his writings beyond Java(see Resources), and the combination of factors that lead to Java's fame: The Rise of the web, the resilience of existing web technologies for a variety of reasons, And the rise of multi-tier application development in enterprises. Tate also believes that the perfect storm is a series of independent events, while other languages do not reach the same heights in the same way.
The Java language has proved its function is quite flexible, but it is well known that its syntax and inherent paradigm have some limitations. Although the Java language is making some seemingly beautiful changes, its syntax simply does not support some important future goals, such as functional programming elements. But if you try to find a new language instead of Java, you're wrong.
Multi-language programming
Multi-language programming is a term I re-introduced and promoted in a 2006 blog post (see Resources), which is based on the perception that a single language is not suitable for all problems. Some languages have intrinsic characteristics that are better suited to specific problems. For example, while swing is as mature as Java, developers find it cumbersome to write the Swing UI in Java because it requires a type declaration that requires the behavior to have an anonymous inner class and other conflicting factors. Using a language better suited to building the UI, such as Groovy with Swingbuilder (see Resources), makes it easier to build Swing applications.
The expansion of the languages running on the JVM makes the idea of multilingual programming more appealing because you can mash it up while maintaining the same underlying bytecode and libraries. For example, Swingbuilder cannot replace Swing; it is layered on the existing Swing API. Of course, for a long time, developers have been mixing languages other than the JVM (for example, mashups using SQL and JavaScript for specific purposes), which is more prevalent within the JVM scope. Many ThoughtWorks projects contain multiple languages, and all tools developed by ThoughtWorks Studios use mixed languages.
Even though Java is still your primary development language, you can learn how to run other languages to use them on a policy. Java is still an important part of the JVM ecosystem, but ultimately people are more inclined to use it as a platform assembly language-a place where you can fully understand performance or meet specific needs.
Development
In the early 1980s, when I was in college, we used a development environment called Pecan Pascal. What makes it unique is that you can run the same Pascal code on Apple II or IBM PCs at the same time. Pecan engineers use something called "bytecode" to accomplish this feat. Developers compile their Pascal code into this "bytecode" and run on "virtual machines" that are written locally for each platform. What a horrible experience! Even for simple tasks, generating code is extremely slow. The hardware at the time was simply unable to meet this challenge.
Ten years after the release of Pecan Pascal, Sun released Java,java used the same architecture, and for the the mid 1990s hardware environment, running the code seemed a bit tense but ultimately successful. Java also adds other developer-friendly features, such as automatic garbage collection. After using a language like C + +, I no longer want to write code in a language that is not garbage collected. I would rather spend time on a higher level of abstraction, thinking about ways to solve complex business problems than wasting time on complex plumbing issues such as memory management.
Computer language usually does not have a long life, one of the reasons is the speed of innovation in language and platform design. As our platforms become more powerful, so do the heavy lifting that can be handled. For example, Groovy's memo feature, an added feature in 2010, buffers The result of a function call. No handwriting buffer code is required, which may introduce a bug, just call the Memoize () method, as shown in Listing 1:
Listing 1. Forgetting a function in Groovy
def static sum = {Number factorsof (number). Inject (0, {i, J-i + J}) } def static sumoffactors = su M.memoize ()
In Listing 1, the results of the Sumoffactors method are automatically cached. You can also use a different method to customize the buffering behavior, such as Memoizeatleast () and Memoizeatmost (). Clojure also provides a memo function, which is irrelevant to Scala's implementation. Advanced features in next-generation languages (and some Java frameworks), such as the memo feature, will gradually find their way into the Java language. The next version of Java will add higher-order functions, making it easier to implement the memo feature. Learn about future Java features in advance by learning the next generation of Java languages.
Groovy, Scala, and Clojure
Groovy is the 21st century Java syntax (espresso, not regular coffee). Groovy's design goal is to update and reduce Java syntax resistance while supporting the main paradigms in the Java language. As a result, Groovy needs to "understand" the technologies such as JavaBeans and simplify property access. Groovy quickly merges new features and provides some important function features, which I'll focus on in a few later installments. Groovy remains essentially an object-oriented, imperative language. The two main differences between groovy and Java are that groovy is static, not dynamic, and its meta-program functions better.
Scala is a language that leverages the advantages of the JVM, but its syntax has been completely redesigned. Scala is a strongly static type language (more restrictive than the more restrictive type requirements) and supports the object-oriented paradigm and function paradigm, and prefers the latter. For example, Scala prefers Val to declare and make immutable variables (similar to marking parameters in Java as final) subject to Var, which creates mutable variables that people are more familiar with. By supporting both paradigms, Scala offers you a bridge from what you might be (an object-oriented, imperative programmer) to something that might be (a programmer in favor of a functional type).
Clojure is a Lisp dialect that completely deviates from other languages in a grammatical way. It is a strongly dynamic type language (like Groovy), reflecting arbitrary design decisions. While Clojure allows you to do full and deep interaction with legacy Java, it does not attempt to build bridges to the old paradigm. For example, Clojure does not have error correction capabilities and supports object-oriented interaction. However, it also supports all the features that an object programmer is accustomed to, such as polymorphism, but it implements these features in a functional way rather than an object-oriented approach. Clojure is designed around some of the core engineering principles (such as software transactional Memory), breaking the old paradigm and supporting new functionality.
Paradigm
In addition to the syntax, the most interesting differences in these languages are the type and underlying primary paradigm: functional or imperative.
Static and dynamic types
A static type in a programming language refers to an explicit type declaration, such as an int x in Java; A dynamic type refers to a language that does not require the provision of type declaration information. All languages are strongly typed, which means that your code can reflect the type of assignment.
Java's type system has been blamed for its static type, which has caused a lot of inconvenience and has not been able to gain significant benefits. For example, before the current finite type inference, Java requires the developer to repeat the type declaration on both sides of the assignment equals sign. Scala is a more static type than Java, but it is not cumbersome to use on a daily basis because it takes advantage of type inference.
Groovy has a behavior that, at first glance, seems to be a bridge between static and dynamic. Consider the simple collection factory shown in Listing 2:
Listing 2. Groovy Collection Factory
Class Collectionfactory { def List getcollection (description) { if (description = = "Array-like") new ArrayList () else if (description = = "Stack-like") new Stack () } }
The class in Listing 2 acts as a factory that returns one of the two list interface implementations (ArrayList or Stack), based on the passed description parameter. For Java developers, this seems to ensure that the results returned meet the rules. However, the two unit tests in Listing 3 show the complications It presents:
Listing 3. Test the collection type in Groovy
@Test void Test_search () { List L = f.getcollection ("stack-like") asserttrue l instanceof java.util.stack< C4/>l.push ("foo") assertthat l.size (), is (1) def r = L.search ("foo") } @Test (expected= Groovy.lang.MissingMethodException.class) void Verify_that_typing_does_not_help () { List L = F.getcollection ("Array-like") asserttrue l instanceof java.util.ArrayList l.add ("foo") Assertthat L.size (), is (1) def r = L.search ("foo") }
In the first unit test in Listing 3, I retrieved the stack from the factory, verifying that it was really a stack, and then performing the operations of the stack, such as push (), size (), and search (). But in the second unit test, I had to use missingmethodexception expected exception to protect the test so that I could pass the test. When retrieving the Array-like set merge and putting it as a list into the variable type, I can prove that I did receive a list. However, when I try to invoke this search () method, it will trigger an exception because ArrayList does not include the search () method. Therefore, by providing a declaration with no compile time protection, you can ensure that the method invocation is correct.
While this may seem like a bug, it is the correct behavior. The type in Groovy only ensures the validity of the assignment statement. For example, in Listing 3, if I return the contents of some list interfaces, a run-time exception (groovycastexception) is triggered. This will give Groovy and Clojure a firm foothold in the strong dynamic type family.
However, recent changes in language have made the static and dynamic distinctions in Groovy very blurred. Groovy 2.0 adds a @TypeChecked annotation that enables you to make strict type-checking special decisions at the class or method level. Listing 4 illustrates this annotation:
Listing 4. Type checking and commenting
@TypeChecked @Test void type_checking () { def f = new collectionfactory () List L = f.getcollection (" Stack-like ") l.add (" foo ") def r = L.pop () assertequals R," foo "}
In Listing 4, I added a @TypeChecked comment that verifies the assignment and subsequent method calls. For example, the code in Listing 5 is no longer compiled :
Listing 5. Type checking to prevent invalid method calls
@TypeChecked @Test void Invalid_type () { def f = new collectionfactory () stack s = (stack) f.getcollection ("s Tack-like ") s.add (" foo ") def result = S.search (" foo ") }
In Listing 5, I have to add a type conversion for the factory return to support my call to the stack's search () method. This tool is used with constraints: When static types are enabled, many of Groovy's dynamic characteristics are not valid. However, this example illustrates the constant change in Groovy when building bridges between static and dynamic partitioning.
All of these languages are very powerful metaprogramming tools, so you can add more restrictive types afterwards. For example, there are auxiliary items that can add a selection type to Clojure. However, typically, if the selection type is optional, it is no longer part of the type system; it is a validation mechanism.
Imperative and functional
Another major comparison is imperative and functional. Imperative Programming focuses on the convenience of stepping instructions, which in many cases mimic the old, low-level hardware. functional Programming focuses on best-in-class structural functions and attempts to minimize state transitions and variability.
The larger Groovy that is affected by Java is actually an imperative language. But from the beginning it contains a lot of functional language features, and over time, new features are being added.
Scala uses and supports these two paradigms together. Although functional programming is preferred (and supported), Scala still supports object-oriented programming and imperative programming. Therefore, proper use of Scala requires a disciplined team to ensure that you do not mix and match paradigms at will, which has been a risky move in a multi-paradigm language.
Clojure does not provide error correction functionality. It supports object-oriented programming to enable easy interaction with other JVM languages, but it does not act as a bridge. Instead, Clojure's arbitrary design claims that designers are thinking about good engineering practices. Those decisions have far-reaching implications, enabling Clojure to address the Java world cumbersome issues (such as concurrency) in groundbreaking ways.
Many of the thinking transformations required to understand these new languages come from imperative/functional partitioning, and this is one of the most significant areas of exploration in this series of articles.
Conclusion
Developers live in a world of more and more languages that solve problems in multiple languages. Learning to use the new language effectively helps you determine when a method is applicable. You don't have to abandon Java, it will gradually fuse the features of the next generation of JVM languages, and by understanding them you can peek into the future of the Java language.
In the next Java Next generation language, I'll start comparing the similarities between Groovy, Scala, and Clojure.
Java Next Generation: Groovy, Scala, and Clojure