1.Java 8新特性概述(一)

前言

       这一节将简单介绍Java 8的,如何使用默认接口方法,Lambda表达式,方法引用和重复注解。

接口中使用默认方法实现

       Java 8允许使用default关键字,在接口中添加非抽象方法。这个特性又被称为 扩展方法。在下面的代码中,formula对象以匿名对象的形式实现了Formula接口;
interface Formula {
    double calculate(int a);      //抽象方法

    //Formula的实现类只需要实现抽象方法caculate就可以了。默认方法sqrt可以直接使用。
    default double sqrt(int a) {  //默认实现方法
        return Math.sqrt(a);
    }
}
Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);    
formula.sqrt(16);          

Lambda表达式

       我们用举例子的方式来讲解Lambda表达式;例如,对一个String列表进行排序。
       在Java 8之前,我们是这样解决的;

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});
       静态工具方法Collections.sort接受一个list,和一个Comparator接口作为输入参数,Comparator的实现类可以对输入的list中的元素进行比较。通常情况下,你可以直接用创建匿名Comparator对象,并把它作为参数传递给sort方法。

       在Java 8中,提供的Lambda表达式可以让这个过程简化;
Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});
//还可以更简单
Collections.sort(names, (String a, String b) -> b.compareTo(a));
       只要一行代码,包含了 方法体。你甚至可以连大括号对 {}return关键字都省略不要。不过这还不是最短的写法,Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写。
Collections.sort(names, (a, b) -> b.compareTo(a));

函数式接口

       接口里只有一个抽象方法(当然可以有任意多个默认方法,default)的接口叫函数式接口。Lambda表达式能通过一个函数式接口进行类型匹配。
       在编写代码的时候,通过在接口前添加@FunctionalInterface注解,可以让编译器对接口进行检查,如果有超过一个的抽象方法就会抛出异常。

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);                   // 123

方法和构造函数引用

        上面的代码还可以这样改:

//这里直接省略掉了参数部分,直接写方法体了(Integer::valueOf)
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);         // 123
        可以通过 ::关键字获取对方法或者构造函数的的引用。上面的例子就演示了如何通过类名来引用一个静态方法。我们也可以通过对象名对一个对象的方法进行引用:
class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}

Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

        下面让我们看看如何使用::关键字引用构造函数。首先我们定义一个示例bean,包含了不同的构造方法:

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

//工厂类接口
interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

//通过Person::new来创建一个Person类构造函数的引用;
//Java编译器会自动地选择合适的构造函数来匹配PersonFactory.create函数的签名
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

Lambda的范围

访问局部变量

//我们可以访问Lambda表达式外部的final局部变量
final int num = 1;
Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3
//与匿名对象不同的是,变量num并不需要一定是final,下面的代码依然是合法的
int num = 1;
Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3
//num在编译的时候被隐式地当做final变量来处理,下面的代码就不合法
//在lambda表达式内部企图改变num的值也是不允许的
int num = 1;
Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);
num = 3;

访问成员变量和静态变量

//与局部变量不同的是,我们在lambda表达式的内部能获取到对成员变量或静态变量的读写权。这种访问行为在匿名对象里也是非常典型的。
class Lambda4 {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Converter<Integer, String> stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };

        Converter<Integer, String> stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}

访问默认接口方法

//无法在Lambda表达式内部访问默认接口方法
Formula formula = (a) -> sqrt( a * 100);

内置函数式接口

       JDK 1.8 API中包含了很多内置的函数式接口。有些在Java 8之前,大家就耳熟能详了,例如Comparator接口,Runnable接口等等。
       对这些现成的接口进行实现,可以通过@FunctionalInterface 标注来启用Lambda功能支持。
       此外,Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。

Predicates

//Predicate是一个布尔类型的函数,该函数只有一个输入参数
//Predicate接口包含了多种默认方法,用于处理复杂的逻辑(and, or,negate)
Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

Functions

//Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Suppliers

//Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

Consumers

//Consumer代表了在一个输入参数上需要进行的操作。
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));

Comparators

//Comparator接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法。
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0

Optionals

       Optional不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerException产生。这个概念在下一节会显得很重要,所以我们在这里快速地浏览一下Optional的工作原理。
       Optional是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返回一个non-null的值,也可能返回一个空值。为了不直接返回null,我们在Java 8中就返回一个Optional.
Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值