1. Reference materials
- [Java8 Practical] E-book download in the attachment
2. Content depth
- Simple, does not involve complex and advanced usage
- This section describes the easiest and most effective Java8 features
3. The most useful features of Java8
Lambda expressions and method references make code more concise and reduce the number of lines of code.
2. Stream, as an advanced iterator of data set, can easily traverse, filter and count a set of data. Parallel processing is supported.
3. Interface Supports the default method and can be flexibly extended.
4. Replace null with Optional to reduce null pointer exceptions.
4. How to utilize Java8 features in AS
- Just configure build.gradle directly in the moudle where you want to use Java8 features
android { *** compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } Copy the code
- Directly search the entire project for “anonymous inner classes that can be replaced with lambda expressions” to reduce the amount of code in one click
- Android Studio toolbar -> Analyze -> Run Inspection by Name
- Press press with lambda. Select Anonymous type can be press with lambda
- Select Whole Project -> OK for the scope
- After the query of AS is complete, run Replace with lambda.
- Here is:
Below, according to the book “Java 8 Practical” chapter order, selected part of the content in turn.
5. How has our code changed since the introduction of Java8: simply put, the amount of code has decreased since the introduction of Lambda.
Flow, method references, options, and so on are stated later.
- The following uses Thread creation as an example
// The original way Thread thread1 = new Thread(new Runnable() { @Override public void run(a) { Log.d(TAG, "old"); }});/ / Java8 writing Thread thread2 = new Thread(() -> Log.d(TAG, "java8")); Copy the code
- Take setting the View click listener as an example
// The original way view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "old"); }});/ / Java8 writing view.setOnClickListener(v -> Log.d(TAG, "java8")); Copy the code
6. The interface supports the default method
1. What is the default method
- Interface in the default modifier, the interface itself has implemented the method.
- The default method in interface I, whose implementation class C inherits directly, does not have to be implemented by C again.
- The instance
public interface I{ void exe(a); // The default method, the interface itself is implemented default void f1(a){ Log.d("Interface"."default Method"); }}public class C implements I{ @Override public void exe(a) {}// The interface implementation class does not need to be implemented } Copy the code
2. Why provide default methods
- The primary purpose of the default method is to allow library designers to extend the existing interface with confidence. To avoid adding new methods to the existing interface, all implementation classes should add their implementation.
- In addition to facilitating interface extension, their own use when doing code reduction
Sometimes define an interface, for the sake of extensibility will declare some methods, existing functions will not be called, or different scenarios, will call different methods;
We will use multiple implementation class instances/anonymous inner classes of this interface for passing parameters.
If the interface is filled with traditional abstract methods, it can lead to bloated, or redundant, code.
By declaring the default method in the interface, you can not only retain the methods to be extended, but also more flexibly rewrite different default methods, greatly reducing the amount of code.
public interface OriInterface { void f1(a); void f2(a); void f3(a); void f4(a); } public interface DefaultInterface { void f1(a); default void f2(a){}; default void f3(a){}; default void f4(a){}; } public void testOriInterface(OriInterface oriInterface){}public void testOriInterfaceFunc2(OriInterface oriInterface){ oriInterface.f2(); } public void testDefaultInterface(DefaultInterface defaultInterface){}public void testDefaultInterfaceFunc2(DefaultInterface defaultInterface){ defaultInterface.f2(); } public void testInterface(a){ // Test the raw interface testOriInterface(new OriInterface() { @Override public void f1(a) {}@Override public void f2(a) {}@Override public void f3(a) {}@Override public void f4(a) {}}); testOriInterfaceFunc2(new OriInterface() { @Override public void f1(a) {}@Override public void f2(a) { Log.d("TestInterface"."ori interface. f2."); } @Override public void f3(a) {}@Override public void f4(a) {}});// Test the interface that contains the default method testDefaultInterface(new DefaultInterface() { @Override public void f1(a) {}}); testDefaultInterfaceFunc2(new DefaultInterface() { @Override public void f1(a) {}@Override public void f2(a) { Log.d("TestInterface"."default interface. f2."); }}); }Copy the code
Lambda expressions
1. What is a Lambda expression
- Lambda expressions are a way to succinctly represent anonymous functions that can be passed.
- Lambda expressions have no name, but have a list of arguments, function topics, return types, and possibly a list of exceptions that can be thrown.
- Lambda expressions can be passed to methods as arguments.
- What about passing as a parameter
The most common way to pass a block of logic/code around is to define an interface I with an instance of I in the method func argument and a method that calls the specified instance of I in the func method body, the common anonymous inner class/listener writing.
public interface I{ void exe(a); } public void func(I i){ i.exe(); } func(new I() { @Override public void exe(a) { Log.d("I"."exe"); }});Copy the code
Lambda expressions are a simplification of the anonymous inner class/listener interface’s bloated code.
func(()->Log.d("I"."exe")); Copy the code
Lambda expressions don’t let you do things you couldn’t do before
2. Several styles of Lambda expressions
- General style: (Parameter list) -> {code block; return **; }
(Parameter list) -> Single line of code (parameter list) -> {code; } (parameter list) -> {code;returnThe return value. } (parameter list) -> {returnThe return value. } (parameter list) -> Return valueCopy the code
Where Lambda expressions can be used: on functional interfaces.
- What is a functional interface?A functional interface is one that has only one abstract method
- Note that the limit applies only to the number of abstract methods.
- An interface is functional even if it contains many default /default methods and defines only one abstract method in the interface.
- DefaultInterface has many default methods, but contains only one abstract method, which is also a functional interface.
@FunctionalInterface public interface DefaultInterface { void f1(a); default void f2(a){}; default void f3(a){}; default void f4(a){}; } Copy the code
- For functional interfaces, the @functionalInterface annotation is recommended, but not required.
- Lambda expressions allow implementations of abstract methods of functional interfaces to be provided directly inline, andTake the entire Lambda expression as an instance of this functional interface.
- Thus, passing an instance of a specified interface using an anonymous inner class is essentially the same as passing an instance using a Lambda expression.
- Lambda expressions just make the code cleaner.
- The signature of an abstract method of a functional interface is essentially the same as the signature of a Lambda expression.
- According to this rule, the corresponding Lambda expression can be written against the ‘method form’ of a functional interface
- (type p1,type p2, ***) -> {method body; }
@FunctionalInterface public interface FuncI1{ void f(a); } @FunctionalInterface public interface FuncI2{ void f1(int p1,int p2); } private void testLambdaFuncSign(a) { // Take Runnable as an example //Runnable is a functional interface //@FunctionalInterface //public interface Runnable { Runnable r1 = new Runnable() { @Override public void run(a) {}}; Runnable r2 = () -> { };// Take a custom functional interface as an example FuncI1 funcI11 = new FuncI1() { @Override public void f(a) {}}; FuncI1 funcI12 = () -> {}; FuncI2 funcI21 =new FuncI2() { @Override public void f1(int p1, int p2) {}}; FuncI2 funcI22 = (p1, p2) -> {}; }Copy the code
4. New functional interfaces in Java8
- Predicate
- Predicate takes a generic T instance and returns a Boolean value. T->boolean.
- Predicate is often used for filtering data in a stream, and other scenarios where a Boolean is returned based on an instance
- The sample
@FunctionalInterface public interface Predicate<T> { boolean test(T t); } public class Person{ public String name = "Li Lei"; public String getName(a) { return name; } public void setName(String name) { this.name = name; }}@RequiresApi(api = Build.VERSION_CODES.N) public boolean testPerson(Person person, Predicate<Person> predicate){ return predicate.test(person); } @RequiresApi(api = Build.VERSION_CODES.N) public void testPredicate(a){ Predicate<Person> personPredicate = person -> person.getName().equals("Han Meimei"); testPerson(new Person(),personPredicate); } Copy the code
- Consumer
- The Consumer receives an instance of generic T with no return. T->void
- As you can see from the naming, simply consume an instance.
- The sample
@FunctionalInterface public interface Consumer<T> { // Simply consume 1 instance void accept(T t); } @RequiresApi(api = Build.VERSION_CODES.N) public void testConsumer(a){ List<String> list = Arrays.asList("1"."2"."3"); Consumer<String> consumer = s -> Log.d("TestConsumer"."Current item :" + s); list.forEach(consumer); } Copy the code
- Function
- Function receives 1 instance of the generic T and returns 1 instance of the generic R. T->R
- Can be understood as a regular input -> output.
- The sample
@FunctionalInterface public interface Function<T.R> { // Receive T instance, return R instance R apply(T t); } @RequiresApi(api = Build.VERSION_CODES.N) public void testFunction(a){ /* Function
function = new Function ,> // Again, use Lambda expressions to represent instances of functional interfaces Function<String, Integer> function = s -> Integer.parseInt(s); int result = function.apply("1000"); } Copy the code() { @Override public Integer apply(String s) { return Integer.parseInt(s); }}; * / ,> - Supplier
- The Supplier does not accept the instance and generates a generic T instance directly. Void ->T
- As you can tell by the name, it’s a producer
- The sample
@FunctionalInterface public interface Supplier<T> { T get(a); } @RequiresApi(api = Build.VERSION_CODES.N) public void testSupplier(a) { Supplier<Person> supplier = () -> new Person(); Person person = supplier.get(); } Copy the code
5. Method references
- Method references let you create Lambda expressions based on existing methods.
- Format of method references: Target reference :: method name
- There are three main types of method references
- A method reference to a static method
- ClassName ::func
- A method reference that points to a method specified by any class instance
- ClassName ::func
- A method reference to a specified method of an existing instance
- Instance ::func
public class PersonOpt{ private String gainTag(Person person){ return "Tag"+ person.getName(); }}@RequiresApi(api = Build.VERSION_CODES.N) public void testMethodReference(a) { // method references to static methods // This refers to a static method of the Integer class // Essence: classname.func (param) // Form: className::func Function<String,Integer> f1 = (s) -> Integer.parseInt(s); Function<String,Integer> f2 = Integer::parseInt; // a method reference that points to the specified method of any class instance // This refers to the non-static getName method in the Person class // Essence: param.func() // Form: className::func Function<Person,String> f3 = (p) -> p.getName(); Function<Person,String> f4 = Person::getName; // a method reference to the specified method of an existing instance // This points to the non-static method PersonOpt of the PersonOpt instance // Essence: instance.func(param) // Instance ::func PersonOpt personOpt = new PersonOpt(); Function<Person,String> f5 = new Function<Person, String>() { @Override public String apply(Person person) { returnpersonOpt.gainTag(person); }}; Function<Person,String> f6 = personOpt::gainTag; }Copy the code
- A method reference to a static method
- Reference to the constructor: ClassName::new
- Constructor references are suitable for producers/suppliers
- The constructor is of the form void->T
@RequiresApi(api = Build.VERSION_CODES.N) public void testConstructorRef(a){ Supplier<Person> supplier1 = new Supplier<Person>() { @Override public Person get(a) { return newPerson(); }}; Supplier<Person> supplier2 = () ->new Person(); // Public Person(){} the constructor is void -> T Supplier<Person> supplier3 = Person::new; } Copy the code
- How do Lambda expressions and method references work
This section uses a custom requirement as an example.
Many of the methods we use take arguments in and return results.
For example, enter three parameters and return one result.
There are many methods of the same form:
public String f1(int p1,long p2, double p3){ return* *; }public int f2(int p1,String p2,Person p3){ return* *; }public Person f3(int p1,long p2,Dog dog){ return* *; }Copy the code
Using lambda expressions and function references, ‘formal reuse’ is possible. Reduce code.
- The corresponding functional interface is first created based on the form of the input parameter and return result. For example, you can define a functional interface that contains four generic data, with three incoming parameters that return one result.
- Then use a Lambda expression to create a corresponding instance of the functional interface. Avoid declaring too many methods.
//1: construct a functional interface/interface with only one abstract method @FunctionalInterface public interface CustomInterface<Source.Param1.Param2.Result>{ Result gainResult(Source source,Param1 param1,Param2 param2); } //2: Creates an instance of the functional interface public void testCustomInterface(a){ // Create the original form CustomInterface<String,Integer,Integer,String> c1 = new CustomInterface<String, Integer, Integer, String>() { @Override public String gainResult(String s, Integer param1, Integer param2) { returns.substring(param1,param2); }};// Lambda expression creation //(parameter list) -> {return result; } CustomInterface<String,Integer,Integer,String> c2 = (source,param1,param2) -> {returnsource.substring(param1,param2); };//(parameter list) -> result CustomInterface<String,Integer,Integer,String> c3 = (source,param1,param2) -> source.substring(param1,param2); // Use method references directly //className::func // The method reference here belongs to the second type: param.func() //param.func() variant param1.func(param2,param3) CustomInterface<String,Integer,Integer,String> c4 = String::substring; } Copy the code