Android与Java8那些事

Lambda表达式

Lambda定义

Why

当使用匿名类的时候,如果这个匿名类十分简单(例如只有一个接口),这样代码看起来较为笨重和不简洁。在使用此类只有一个接口的匿名类的过程中,我们往往把它作为一个参数传递下去,例如button点击事件,而Lambda表达式可以有效解决此类问题。

What

Lambda 表达式的语法格式如下:

(parameters) -> expression

(parameters) ->{ statements; }

Lambda表达式的几个特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda使用

public class LambdaTester {
   public static void main(String args[]){
      LambdaTester tester = new LambdaTester();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括号
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

输出结果:

10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

再比如使用Lambda实现 Runnable接口 :

// 1.1使用匿名内部类  
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
}).start();  
  
// 1.2使用 lambda expression  
new Thread(() -> System.out.println("Hello world !")).start();  
  
// 2.1使用匿名内部类  
Runnable race1 = new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
};  
  
// 2.2使用 lambda expression  
Runnable race2 = () -> System.out.println("Hello world !");  
   
// 直接调用 run 方法(没开新线程哦!)  
race1.run();  
race2.run();  

可以看出Lambda表达式可以将代码变得十分简洁。

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量

public class LambdaTester {
 
   final static String salutation = "Hello! ";
   
   public static void main(String args[]){
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Runoob");
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
}

这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2);  // 输出结果为 3
    }
 
    public interface Converter {
        void convert(int i);
    }
}

int num = 1;  
Converter s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量

String first = "";  
Comparator comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

方法引用

方法引用定义

What

方法引用是一个Lambda表达式,可以直接访问类或者实例的已经存在的方法或者构造方法。
方法引用的操作符是双冒号"::"。

方法引用使用

方法引用提供了四种实现方式:

类型 示例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用特定类型的任意对象的实例方法 ContainingType::methodName
引用构造函数 ClassName::new

引用静态方法

举个栗子:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }
    
    public Calendar getBirthday() {
        return birthday;
    }    

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }}

假设我们一个Person数组,需要进行排序,那么需要进行以下逻辑:

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

class PersonAgeComparator implements Comparator {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
        
Arrays.sort(rosterAsArray, new PersonAgeComparator());

这里可以看下Arrays的sort方法:

static  void sort(T[] a, Comparator c)

这时,可以使用前面说到的Lambda表达式来进行转化:

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

或者使用单行Lambda表示:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

这里能否将code进一步简化了呢?此节中方法引用可以解决这个问题:

Arrays.sort(rosterAsArray, Person::compareByAge);

这个也是四种方法的第一种:引用静态方法。

引用某个对象的实例方法

此方法和引用静态方法类似,只不过这里使用对象引用而不是类名。这里用上面的栗子稍微改动一下:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
        
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

引用特定类型的任意对象的实例方法

以下是对特定类型的任意对象的实例方法的引用示例:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

这里的String::compareToIgnoreCase将会有(String a, String b)两个参数,方法引用会调用a.compareToIgnoreCase(b)。

引用构造函数

构造方法引用又分构造方法引用和数组构造方法引用。

构造方法引用

e.g String::new, 等价于lambda表达式 () -> new String()

数组构造方法引用

TypeName[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。等价于lambda表达式 x -> new TypeName[x]。举个栗子:

IntFunction arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 创建数组 int[10]

默认和静态接口函数

What

简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。

How

默认方法

默认方法语法格式如下:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}

多个接口时,如何处理呢?

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}
 
public interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮汽车!");
   }
}

第二种解决方案可以使用 super 来调用指定接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}

静态默认方法

Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    // 静态方法
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}

那如何使用呢?接下来举个例子:

public class Java8Tester {
   public static void main(String args[]){
      Vehicle vehicle = new Car();
      vehicle.print();
   }
}
 
interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}
 
interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}
 
class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
      FourWheeler.super.print();
      Vehicle.blowHorn();
      System.out.println("我是一辆汽车!");
   }
}

执行以上脚本,输出结果为:

我是一辆车!
我是一辆四轮车!
按喇叭!!!
我是一辆汽车!

到这里,Android中Java8常用新特性已经讲完了,大家在开发过程中会使用新特性的频率有多少呢?
参考资料:
http://www.runoob.com/java/java8-lambda-expressions.html
http://www.cnblogs.com/franson-2016/p/5593080.html

你可能感兴趣的:(Android与Java8那些事)