Java 8 vs. Scala (i): lambda expressions

Source: Internet
Author: User
Tags stream api



"Editor's note" Although Java has won a lot of developers love, but compared to other modern programming languages, its syntax is indeed slightly lengthy. But with Java8, you can write code that is both readable and concise by using lambda expressions directly. The author Hussachai Puripunpinyo's software engineer, who analyzed the differences in performance and expression by comparing Java 8 and Scala, and discussed in depth the differences between the Stream APIs, compiled and collated by ONEAPM engineers.



For several years, Java 8 finally added the feature of higher-order functions. I like Java very much, but I have to admit that Java syntax is very verbose compared to other modern programming languages. With Java8, however, you can write code that is both readable and concise (sometimes even more readable than traditional methods) by using lambda expressions directly.



Java 8 was released on March 3, 2014, but I have recently had the opportunity to contact. Because the author is also familiar with Scala, there is a comparison between the expressive and performance differences between Java 8 and Scala, which will be expanded around the stream API, as well as how to manipulate the collection using the Stream API.



Because the article is too long, the following three sections are described in detail.



Part 1.LAMBDA expression



Part 2. Stream API vs Scala Collection API



Part 3. Trust no one, bench everything (quoted from SBT-JMH)



First, let's look at the lambda expression under Java 8, although it's not known that even though the expression parts are replaceable, they're called lambda expressions. It is entirely possible to replace an expression with a declaration, and then say that Java 8 also supports lambda declarations. A programming language takes a function as a class citizen, and a function can be passed as a parameter or a return value because it is treated as an object. Java is a static, strongly typed language. Therefore, a function must have a type, so it is also an interface.



On the other hand, a lambda function is a class that implements a function interface. Without creating a class for this function, the compiler will implement it directly. Unfortunately, Java does not have an advanced type of interface like Scala. If you want to declare a lambda expression, you must specify the target type. In fact, Java has to be backwards compatible, which is understandable, and Java is doing well for now. For example, Thread.stop () was released in JDK version 1.0, which has been obsolete for more than 10 years, but is still in use even today. So, don't expect Java to fundamentally change the grammatical structure because of the language XYZ syntax (or method) is better.



So, the language designers of Java 8 are thinking, make a function interface! A function interface is an interface that has only one abstract method. You know, most callback interfaces already meet this requirement. Therefore, we can reuse these interfaces without making any modifications. @FunctionalInterface is a comment that indicates that the annotated interface is a function interface. This comment is optional and does not need to be processed unless there is a check request.



Keep in mind that a lambda expression must define a type, and that type must have only one abstract method.


//Before Java 8
Runnable r = new Runnable(){  
  public void run(){    
    System.out.println(“This should be run in another thread”);  
  }
};

//Java 8
Runnable r = () -> System.out.println(“This should be run in another thread”);


What if a function has one or more parameters and has a return value? To solve this problem, Java 8 provides a series of common function interfaces in thejava.util.functionpackage.


//Java 8Function<String, Integer> parseInt = (String s) -> Integer.parseInt(s);


The parameter type can be inferred from the function, like the diamond operator in Java7, so it can be omitted. We can rewrite the function as follows:


//Java 8Function<String, Integer> parseInt = s -> Integer.parseInt(s);


What if a function has two parameters? Don't worry, Java 8 has bifunction.


//Java 8BiFunction<Integer, Integer, Integer> multiplier =   (i1, i2) -> i1 * i2; //you can’t omit parenthesis here!


What if a function interface has three parameters? Trifunction? The language designer stops at Bifunction. Otherwise, there may really be trifunction, quadfunction, pentfunction and so on. Explain, the author uses the IUPAC rule to name the function. The trifunction can then be defined as follows.


//Java 8@FunctionalInterfaceinterface TriFunction<A, B, C, R> {    public R apply(A a, B b, C c);}


Then import the interface and use it as a lambda expression type.


//Java 8TriFunction<Integer, Integer, Integer, Integer> sumOfThree   = (i1, i2, i3) -> i1 + i2 + i3;


Here you should be able to understand why the designer stops at Bifunction.



If you don't understand it, take a look at pentfunction, assuming we've defined pentfunction somewhere else.


//Java 8
PentFunction<Integer, Integer, Integer, Integer, Integer, Integer> 
  sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;


Do you know how long ennfunction is? (in Latin, Enn says 9) You have to declare 10 types (the first 9 are parameters, the last one is the return type), and probably the entire line has only one type. Is it necessary to declare a type? The answer is yes. (That's why I think Scala's type interface is better than Java)



Scala also has its lambda expression type. In Scala, you can create a lambda expression with 22 parameters, meaning that Scala has the type of each function (Function0, Function1 、...... FUNCTION22). The function type is a trait,trait in the Scala function, like an abstract class in Java, but can be used as a mixed type. If you need more than 22 parameters, it's probably a problem with the design of your function. You must consider the type of a set of arguments that are passed. Here, I will not dwell on the details of LAMBDA expressions.



Let's look at other things in Scala. Scala is also a Java-like static strongly typed language, but it starts out as a functional language. As a result, it blends object-oriented and functional programming well. Because of the different methods used by Scala and Java, it is not possible to give a Runnable Scala instance. Scala has its own solution to the problem, so it will be explored in detail next.



Scala Future (println{"This should is run in another thread"})



is equivalent to the following JAVA8 code.



Java 8//assume that is instantiated executorservice beforehand. Runnable r = ()-SYSTEM.OUT.PRINTLN ("This should is run in another thread");
Executorservice.submit (R);



If you want to declare a lambda expression, you do not have to declare an explicit type like Java.


//Java 8
Function<String, Integer> parseInt = s -> Integer.parseInt(s);

//Scala
val parseInt = (s: String) => s.toInt
//or
val parseInt:String => Int = s => s.toInt
//or
val parseInt:Function1[String, Int] = s => s.toInt


So there are many ways to declare a type in Scala. Let the compiler do that. What about Pentfunction?


//Java 8
PentFunction<Integer, Integer, Integer, Integer, Integer, Integer> sumOfFive 
  = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;

//Scala
val sumOfFive = (i1: Int, i2: Int, i3: Int, i4: Int, i5: Int) => 
  i1 + i2 + i3 + i4 + i5;


Scala is shorter because there is no need to declare an interface type, and the integer type is int in Scala. Short does not always mean better. Scala's approach is better, not because it's short, but because it's more readable. The context of the type is in the parameter list and you can quickly find the parameter type. If you are not sure, you can refer to the following code again.


//Java 8
PentFunction<String, Integer, Double, Boolean, String, String> 
  sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;

//Scala
val sumOfFive = (i1: String, i2: Int, i3: Double, i4: Boolean, i5: String) 
=> i1 + i2 + i3 + i4 + i5;


In Scala, it is clear to say that the i3 type is Double, but in Java 8, you also need to figure out what type it is. You may argue that Java is also possible, but this situation occurs:


//Java 8
PentFunction<Integer, String, Integer, Double, Boolean, String> sumOfFive 
  = (Integer i1, String i2, Integer i3, Double i4, Boolean i5) 
  -> i1 + i2 + i3 + i4 + i5;


You have to repeat it over and over again.



In addition, JAVA8 does not have pentfunction and needs to define itself.


//Java 8
@FunctionalInterface
interface PentFunction<A, B, C, D, E, R> {  
  public R apply(A a, B b, C c, D d, E e);
}


Does that mean Scala is better? In some ways, indeed. But there are many places where Scala is inferior to Java. So it's hard to tell which one is better, and I'm comparing them because Scala is a functional language, and Java 8 supports some function features, so we have to look at the function language to compare it. Since Scala can run on the JVM, it's better to compare it. Maybe when you use a function, Scala has a more concise syntax and method, because it is a functional language, and the Java designer expands the design without destroying it, apparently with more restrictions.



Although Java has some limitations in syntax compared to lambda expressions, JAVA8 has introduced some cool features. For example, using the attributes referenced by a method makes writing a lambda expression more concise by reusing existing methods. Is it more concise???


//Java 8Function<String, Integer> parseInt = s -> Integer.parseInt(s);


You can use a method reference to override the function, as shown below


//Java 8Function<String, Integer> parseInt = Integer::parseInt;


You can also use method references by instance methods. The availability of this method is then indicated in the Stream API in the second section.



Construction rules for method references



1. (args), Classname.staticmethod (args);



Can rewrite classname::staticmethod like this;



Function<Integer, String> intToStr = String::valueOf;



2. (instance, args), Instance.instancemethod (args);



Can rewrite classname::instancemethod like this;



BiFunction<String,String, Integer> indexOf = String::indexOf;



3. (args), Expression.instancemethod (args);



Can rewrite expression::instancemethod like this;



Function<String, Integer>indexOf = new String()::indexOf;



Have you noticed that Rule 2 is a little strange? A little confusing? Although the INDEXOF function requires only 1 parameters, the target type of bifunction is 2 parameters. In fact, this usage is usually used in the Stream API, which makes sense when the type name is not visible.


pets.stream().map(Pet::getName).collect(toList());
// The signature of map() function can be derived as
// <String> Stream<String> map(Function<? super Pet, ? extends String> mapper)


From Rule 3, you might wonder can you replace the new String () with a lambda expression?



You can construct an object in this way



Supplier<String> str =String::new;



So can you do that?



Function<Supplier<String>,Integer> indexOf = (String::new)::indexOf;



No. It cannot be compiled and the compiler will promptThe target type of this expression must be a functional interface. Error messages can easily be misunderstood, and it seems that Java 8 does not support type interfaces through generic parameters. Even if you use an instance of Functionalinterface (as mentioned earlier "str"), another error will occurThe type Supplier<String> does not define indexOf(Supplier<String>) that is applicable here. The function interface of String::new is Supplier, and it is only named by the method get (). IndexOf is an instance method that belongs to a String object. Therefore, you must override this code, as shown below.


//JavaFunction<String, Integer> indexOf =          ((Supplier<String>)String::new).get()::indexOf;


Does Java 8 support currying (partial function)?



Yes, but you can't use a method reference. You can think of it as a partial function, but it returns a function instead of a result. Then we will introduce a simple example of using currying, but this example may not work. We usually do the parameter processing before passing it to the function. But in any case, first look at how to implement the partial function with a lambda expression. Suppose you need to use currying to implement a function that adds two integers.


//Java
IntFunction<IntUnaryOperator>add = a -> b -> a + b;
add.apply(2).applyAsInt(3);//the result is 4! I‘m kidding it‘s 5.


The function can take two parameters at a time.


//Java
Supplier<BiFunction<Integer,Integer, Integer>> add = () -> (a, b) -> a + b;
add.get().apply(2, 3);


Now you can look at the Scala method.


//Scala
val add = (a: Int) => (b:Int) => a + b
add(1)(2)

//Scala
val add = () => (a: Int,b: Int) => a + b
add2()(1,2)


Scala's approach is shorter than Java because of the type reference and syntax sugar. In Scala, you don't need to raise the Apply method on Function trait, and the compiler will immediately convert () to the Apply method.



Original link: https://dzone.com/articles/java-8-λe-vs-scalapart-i



OneAPM for Java is able to perform application performance management and monitoring within all Java applications, including visibility of code-level performance issues, rapid identification and traceability of performance bottlenecks, real user experience monitoring, server monitoring, and end-to-end application management. To read more technical articles, please visit the OneAPM official blog.



Turn from: http://news.oneapm.com/java8-lamnda/more: http://www.oneapm.com/brand/apm.html



Java 8 vs. Scala (i): lambda expressions


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.