JAVA8 new feature functional programming Lambda

JSR(Java Specification Requests) 335 = lambda expression + interface improvement (default method) + bulk data manipulation

1. Functional programming
deals with code in a way that deals with data, which means functions can be assigned to variables, passed to functions, etc. Functions are first-level values.
Introductory example:

[java] view plain copy

  1. import java.util.List;  
  2. public class Test1 {  
  3.     public String cleanNames(List<String> listOfNames) {  
  4.         StringBuffer sb = new StringBuffer();  
  5.         // Only the elements in the List can be processed sequentially, each time a filter, transformation and reduce(join) are performed  
  6.         for(int i = 0; i < listOfNames.size(); i++) {  
  7.             if(listOfNames.get(i).length() > 1) {  
  8.                 sb.append(capitalizeString(listOfNames.get(i))).append(",");  
  9.             }  
  10.         }  
  11.         return sb.substring(0, sb.length() - 1).toString();  
  12.     }  
  13.   
  14.     public String capitalizeString(String s) {  
  15.         return s.substring(0, 1).toUpperCase() + s.substring(1, s.length());  
  16.     }  
  17. }  

 

Scala/Groovy/Clojure are programming languages ​​on the JVM platform, and they all support functional programming. The following is a functional rewrite of the above code in Scala language:

 

[java] view plain copy

  1. val employees = List("dsds", "d", "gghh", "kmo");  
  2. val result = employees  
  3. .filter(_.length() > 1)  
  4. .map(_.capitalize)  
  5. .reduce(_ + "," + _)   

 

Separating the three behaviors of filtering, transformation and aggregation, on the surface, the above code seems to first traverse the list to pick out all eligible elements and pass them to the map, then the map traverses and processes the results from the filter, and finally the processed The results are passed to reduce, which then traverses the processing. It seems to have gone through many traversals after counting, but its specific implementation is not the case, because it is "lazy", its specific implementation behavior is basically the same as the above JAVA for-loop implementation, but the functional formula is more intuitive and concise. That's it.

Static language VS dynamic language: http://www.zhihu.com/question/19918532

 

Functional programming in JAVA (Lambda):
When we use anonymous inner classes to define different behaviors for an interface, the syntax is too redundant, lambda provides a more concise syntax.
- Functional interfaces
Each function object corresponds to an interface type. A functional interface can only have one abstract method, and the compiler will judge by itself according to the structure of the interface (the judgment process is not a simple count of interface methods, because the interface may also define static or default methods). API authors can also specify an interface as a functional interface through the @FunctionalInterface annotation, and then the compiler will verify whether the interface meets the requirements of a functional interface.

Functional interfaces that already exist in JAVA SE 7, such as:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.util.Comparator
- java.io.FileFilter

JAVA SE 8 adds a new package java. util.function, contains common functional interfaces, such as:
- Predicate<T> -- boolean test(T t);
- Consumer<T> -- void accept(T t);
- Function<T, R> - - R apply(T t);
- Supplier<T> -- T get();

- BiFunction<T, U, R> --  R apply(T t, U u);

 

Example lambda expression:
x -> x+1 
() -> {System.out.println("hello world");}

(int x, int y) -> { x++; y--; return x+y;}

Function<Integer, Integer> func = (x, Integer y) -> x + y //wrong
Function<Integer, Integer> func = Integer x -> x + 1 //wrong


A lambda expression can be assigned to a target type T if and only if all of the following conditions are met:
- T is a functional interface
- The parameters of the lambda expression and the method parameters of T correspond in number and type
- The return value of the lambda expression is compatible with the return value of the method of T
- The exception thrown in the lambda expression and the method throws type of T
can be omitted if the parameter type of the lambda expression can be derived from the target type if it is compatible .
Target type context:
- variable declaration
- assignment
- return statement
public Runnable PrintSth() {
return () -> {
System.out.println("sth");
};

}

 

Comparator<String> c;

c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);

 

- array initializer
new FileFilter[] {
f -> f.exists(), f -> f.canRead(), f -> f.getName().startsWith("s")

}

 

- Parameters of methods and constructors
List<Person> ps = ...;
Stream<String> names = ps.stream().map(p -> p.getName());
The parameters of map are Function<T, R >, p can deduce that T is of type Person from List<Person>, and can know that the type of R is String through Stream<String>. When the compiler cannot parse out the correct lambda type, you can:
1. Provide the lambda parameter Display Type
2. Convert lambda expression to Function<Person, String>

3. Provide an actual type for the generic parameter R, such as .<String>map(p->p.getName())

 

- lambda expression function body
- conditional expression (?:)
- conversion expression
Supplier<Runnable> c = () -> () -> {System.out.println("hi");};
Callable<Integer> c = flag ? (() -> 23) : (() -> 42);
Object o = () -> {System.out.println("hi");}; //Illegal
Object o = (Runnable) () -> {System.out.println("hi");};

Lexical scope:
lambdas do not introduce a new scope.
public class Hello {
  Runnable r1 = () -> { System.out.println(this); };
  Runnable r2 = () -> { System.out.println(toString()); };
  public String toString() { return "Hello, world"; }
  public static void main(String... args) {
    new Hello().r1.run();
    new Hello().r2.run();
  }

}

Output: 

Hello, world

Hello, world


lambdas cannot mask local variables in their context, like:
int i = 0;
int sum = 0;
for (int i = 1; i < 10; i += 1) { //There will be a compile error here because i has been declared outside the for loop
  sum += i;
}
 

Variable capture:

 

[java] view plain copy

  1. import java.util.function.Function;  
  2.   
  3. public class Test3 {  
  4.     int sum = 0;  
  5.     public Function m() {  
  6.         //int sum = 0;  
  7.         Function<Integer, Integer> func= i -> {sum ++; return sum + i;};  
  8.         sum++;  
  9.         return func;  
  10.     }  
  11.       
  12.     public static void main(String[] args) {  
  13.         Test3 t = new Test3();  
  14.         Function<Integer, Integer> func = t.m();  
  15.         //t = null;  
  16.         System.out.println(func.apply(9));  
  17.         System.out.println(t.sum);  
  18.     }  
  19. }  

Output: 11, 2

 

Function<Integer, Integer> func = t.m(); //++this.sum;

System.out.println(func.apply(9)); //++this.sum; this.sum+i;

Lambda can only capture effectively final local variables, that is, the relevant variables cannot be modified during or after capture. If sum is a local variable of m method, func cannot modify the local variable, because m exits the stack frame after execution. At the same time, this can also avoid causing race conditions. In a nutshell, lambda expressions are closed to values ​​and open to variables.

int sum = 0;
list.forEach(e -> { sum += e.size(); }); // Illegal, close over values
List<Integer> aList = new List<>();
list.forEach(e -> { aList.add(e); }); // Legal, open over variables

A race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data.

list.parallelStream().forEach(e -> {sum += e;}); //At this time, multiple threads are processed at the same time. If sum is not immutable, it will cause a race condition, in which case we need to provide synchronization Or use volatile to avoid reading stale data.

http://www.lambdafaq.org/what-are-the-reasons-for-the-restriction-to-effective-immutability/

In a common inner class, the instance will always keep a strong reference to its outer class instance, which may often cause memory leaks. The lambda expression only maintains a this reference when it is useful to the class instance variable, such as the above func will save a reference to an instance of Test3.

 

lambda shorthand - method reference:
If the method we want to call has a name, we can call it directly.
- static method references className::methodName
- instance methods on instances refer to instanceReference::methodName
- instance methods on superclasses refer to super::methodName
- instance methods on types refer to className::methodName, such as String::toUpperCase, syntax The same as static method references, the compiler will make decisions based on the actual situation
- constructors refer to className::new
- array constructors refer to TypeName[]::new

[java] view plain copy

  1. import java.util.function.Function;  
  2. import java.util.function.IntFunction;  
  3.   
  4. public class Test3 {  
  5. public static void typeRef() {  
  6. // public String toUpperCase()  
  7. Function<String, String> func = String::toUpperCase;  
  8. System.out.println(func.apply("aaa"));  
  9. }  
  10.   
  11. public static void arrayRef() {  
  12. IntFunction<int[]> arrayInt = int[]::new;  
  13. int[] array = arrayInt.apply(10);  
  14. System.out.println(array);  
  15. }  
  16.   
  17. public static void main(String[] args) {  
  18. typeRef();  
  19. arrayRef();  
  20. }  
  21. }  


Second, the default method and static interface method
How to add other methods to a single abstract functional interface? Interface methods can be abstract and default, default methods have default implementations, and types that implement the interface can get that default implementation through inheritance. In addition, the interface also allows the definition of static methods, so that we can directly call and its related helper methods from the interface. Default methods can be inherited. When the parent class and the superclass of the interface have multiple methods with the same signature, such as diamond inheritance, we need to follow some rules:
1. The method of the class takes precedence over the default method, regardless of the specific method. Still abstract
2. Methods overridden by other types are ignored, when the superclass shares a common ancestor
class LinkedList<E> implements List<E>, Queue<E> { ... }
3. When two A compilation error will occur when an independent default method or a default method conflicts with an abstract method, in which case we need to explicitly override the superclass method
interface Robot implements Artist, Gun {
  default void draw() { Artist.super.draw(); }
}

 

3. Batch processing

- pass behavior instead of value

enhanced for loop VS forEach

[java] view plain copy

  1. import java.util. *;  
  2. import java.util.function.Consumer;  
  3.   
  4. public class Test2 {  
  5.     static List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  
  6.   
  7.     public static void outer() {  
  8.     // can only process the elements in the collection sequentially  
  9.         for(int number : numbers) {  
  10.             System.out.println(number);  
  11.         }  
  12.     }  
  13.   
  14. // interface Consumer<T>:  
  15. // void accept(T t);  
  16.   
  17. //  interface Iterable<T>:  
  18. // default void forEach(Consumer<? super T> action) {  
  19. //        Objects.requireNonNull(action);  
  20. //        for (T t : this) {  
  21. //            action.accept(t);  
  22. //        }  
  23. //    }  
  24.   
  25. //  Array.asList -> Array$ArrayList  
  26. //  Array$ArrayList extends AbstractList, AbstractList implements List<E>  
  27. //  List extends Collection, Collection extends Iterable  
  28. // public void forEach(Consumer<? super T> action) {  
  29. //   Objects.requireNonNull(action);  
  30. //   for (T e : a) {  
  31. //       action.accept(e);  
  32. //     }  
  33. // }  
  34.    
  35.     public static void inner() {  
  36.         numbers.forEach(value -> System.out.println(value));  
  37.     }  
  38.   
  39.     public static void main(String[] args) {  
  40.     inner();  
  41.     }  
  42. }  

Personal opinion, in this example, forEach is not more efficient than for loop, and generally it only plays a role when it is called under parallel stream.


- Parallel processing, batch processing
Stream is a new key abstraction in the java8 class library, which is defined in java.util.stream. Operations on streams can be combined into pipelines.

Stream features:
1. Stream does not store values, and the elements of the stream come from the data source (data structure, generating function or I/O channel)
2. Operations on the stream do not modify the data source
3. Lazy evaluation, each step only needs As soon as a number that satisfies the condition is found, it is immediately passed to the next step to process and pause the current step
4. Optional upper bound, because the stream can be infinite, the user can keep reading until the upper bound is set.

We can use the collection as the stream's A data source (Collection has stream and parallelStream methods), and a collection can also be generated from a stream (collect method). The data source of the stream may be a mutable collection. If the data source is modified during traversal, an interference will occur, so as long as the collection only belongs to the current thread and the lambda expression does not modify the data source.
1. Don't interfere with the data source 2. Don't interfere with other lambda expressions
, which occurs when one lambda is modifying some mutable state and another lambda is reading the state Filtering of all elements is done before, while lazy evaluation filters when needed, traversing all at once. Collectors: Use the collect() method to aggregate elements in the stream into collections, such as List or Set. collect() takes a parameter of type Collectors, which determines how the elements in the stream are aggregated into other data structures. The Collectors class contains a large number of factory methods for common collectors, of which toList() and toSet() are two of the most common.







Parallelism:
In order to achieve efficient parallel computing, the fork/join model is introduced:
- Break the problem into sub-problems
- Solve the sub-problems serially to get partial results
- Combine partial results into the final result


Parallel Stream: Abstract the stream as a Spliterator, allowing Transfer part of the input elements to another new Spliterator, and the rest of the data will be stored in the original Spliterator. The two Spliterators can then be further split. Split iterators can also provide metadata about the source (such as the number of elements) and a range of other boolean characteristics (such as whether the elements are sorted). If you want to implement a collection or stream yourself, you may need to implement the Spliterator interface manually. Stream is very important in JAVA SE8. We provide stream and parallelStream for collection to convert collection into stream. In addition, array can also be converted into stream through Arrays.stream(). There are also some static factory methods in stream to create streams, such as Stream.of(), Stream.generate(), etc.

- Create
static method of stream 1.Stream interface
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
Stream<String> stringStream = Stream.of("taobao");

2. Default methods of the Collection interface

 

- Convert stream, each conversion will return a new stream object (convert a stream into a new stream through some behavior)
1. distinct, the elements contained in the stream are not repeated
2. filter, use the given filter function Perform the filtering operation
3. map, use the given conversion function to perform the conversion operation on the elements contained in the stream, and the newly generated Stream only contains the elements generated by the conversion. This method has three variants of primitive types, mapToInt, mapToLong and mapToDouble.
4. peek, generate a new stream containing the elements of the original stream, and provide a consumption function. When each element of the new stream is consumed, it will execute the given consumption function
5. limit, truncate a stream

6. skip, returns a new stream that discards the first N elements of the original stream and the remaining elements

 

- Aggregate the stream to get the desired result
Accept a sequence of elements as input, repeatedly use a merge operation to merge the elements in the sequence into a summary result

http://docs.oracle.com/javase/tutorial/ essential/concurrency/forkjoin.html
 

4. Other

Code loading order:

Non-static is to apply for memory space in new and initialize its value. Java must load the bytecode first, and then apply for the memory space of the class according to the bytecode information through new. Static is when the bytecode of the class is loaded into the jvm, but only executed once. Static variables and static code blocks are related to the order of declaration. If a static variable is called in a code block today, the static variable must be declared before the static code block; if the static code block does not call a static variable, then declare who is loaded first.
Parent class static properties - "parent class static code block -" child class static variable - "child class static code block - "parent class non-static variable - "parent class non-static variable - "parent class non-static code block - "parent class Constructor-"Subclass non-static variable-"Subclass non-static code block-"Subclass constructor
 

1. When initializing the construction, parent first, then child
2. Static, non-static
3. Classes in java are loaded only when they are used
4. Java classes can only be constructed after the class bytecode is loaded object instance

 

Recursion + combination and then slowly study:

http://www.cnblogs.com/ldp615/archive/2013/04/09/recursive-lambda-expressions-1.html

http://www.kuqin.com/shuoit/20140913/342127.html

 

At present (2016-8) like JAD, JD-GUI does not support the Lambda feature, so I found a new decompilation tool CFR, just go to the official website to download the CFR jar package, then CMD command to enter the directory where the package is placed, run :

java -jar cfr**.jar *.class (write the corresponding name in the place of *) 

 

Reference:

1. Java Next Generation: Functional Coding Style (Functional structure shared by Groovy, Scala and Clojure and its advantages)
2. http://www.cnblogs.com/figure9/archive/2014/10/24/4048421.html
3 . http://en.lucida.me/blog/java-8-lambdas-insideout-library-features/

4. http://ifeve.com/stream/

5. http://www.oschina.net/question/2273217_217864

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325360091&siteId=291194637