java 默认函数,Java 八 之 默认方法、函数式接口、方法引用

Java 8 之 默认方法、函数式接口、方法引用

默认方法(Default Methods)

在Java的interface中一直只是包含方法的定义不能有实现,而且Java也不支持多继承。参考Why Multiple Inheritance is Not Supported in Java。Java 8开始提供在interface中可以有方法的实现,这个特性叫默认方法“Default Methods”。如此以来,interface有了方法实现,Java即可实现多继承,Java一贯提倡不支持多继承,为什么这次妥协了呢?

因为interface太过依赖于他们的实现类了,要往interface中添加一个方法就必须修改所有它的实现类。Java 8中为了配合增加的Lambda表达式特性需要修改java.util包的接口来添加方法,试想一下如果没有默认方法的话,是不是要修改所有集合类的实现啊。

List> list = …

list.forEach(…);

所以,在不破坏继承关系的前提下,引入“虚拟扩展方法”的概念,在接口中添加默认方法的实现,这也是很聪明的,虽然Java语言在被设计的时候一直强调不支持多继承,看来这次也是没有办法的了,Java也变得越来越像C#了。

interface Person {

// adds a java 8 default method

default void sayHello() {

System.out.println("Hello");

}

}

class Sam implements Person {

}

new Sam().sayHello();

interface Person {

// adds a java 8 default method

default void sayHello() {

System.out.println("Hello");

}

}

interface Male {

default void sayBye() {

System.out.println("Bye");

}

}

class Sam2 implements Person, Male {

}

new Sam2().sayHello();

new Sam2().sayBye();

默认方法重名的时候:

interface Person {

// adds a java 8 default method

default void sayHello() {

System.out.println("Hello");

}

}

interface Male {

default void sayHello() {

System.out.println("Hi");

}

}

class Sam2 implements Person, Male {

// override the sayHello to resolve ambiguity

public void sayHello() {

Male.super.sayHello();

}

}

抽象方法和默认方法重名的时候:

interface Q1 {

abstract void m();

}

interface Q2 {

default void m() {

System.out.println("m@Q2");

}

}

class Sam3 implements Q1, Q2 {

@Override

public void m() {

System.out.println("m@Sam3");

}

}

new Sam3().m(); // m@Sam3

除过以前的那些区别外,带有默认方法的interface和抽象类的区别:

抽象类是有个状态的,它可以有构造函数和成员变量

抽象类不能用于lambda表达式中

默认方法不能覆盖 equals, hashCode, toString 方法!!!

原因可以参考Brian Goetz给出的回答:Allow default methods to override Object's methods

http://viralpatel.net/blogs/java-8-default-methods-tutorial/

http://zeroturnaround.com/rebellabs/java-8-explained-default-methods/

函数式接口(Functional Interfaces)

函数式接口就是只包含一个抽象方法的接口。比如Java标准库中的java.lang.Runnable和java.util.Comparator都是典型的函数式接口。使用注解 @FunctionalInterface 可以显示指明一个接口是函数式接口,但如果接口中只定义了一个抽象方法,通常不用自己写这个注解,编译器会自动识别它就是函数式接口。因为默认方法不是抽象的,所以函数接口里还可以定义任意多的默认方法。实质就是 C# 的委托(delegate)。

定义

interface MyFi1 {

public abstract void run();

}

@FunctionalInterface

interface MyFi2 {

public abstract void run();

}

@FunctionalInterface

interface MyFi3 {

public abstract void run();

default void tripleRun(){

run();run();run();

}

}

实现

// 匿名类实现

new MyFi1() {

@Override

public void run() {

System.out.println("Anonymous Inner class");

}

}.run();

// lambda表达式实现只需要提供形式参数和方法体。

// 函数式接口只有一个抽象方法,编译器能自动推断方法。

MyFi1 dbl = () -> System.out.println("Lambda Expression");

dbl.run();

如果接口有多个抽象方法,会提示“The target type of this expression must be a functional interface”。

Java 8在java.util.function中定义了很多常用的函数类型接口。

分类

函数:Function / BiFunction  T -> R

运算:UnaryOperator / BinaryOperator T -> T 或 (T, T) -> T

预言:Predicate / BiPredicate

生产者:Supplier  返回T

消费者:Consumer / BiConsumer 只有一个参数

最常用的:

1个参数一个返回值:Function

1个参数没有返回值:Consumer

没有参数一个返回值:Supplier

对于int/long/double类型还有特别的封装。

Function len = s -> s.length();

System.out.println( len.apply("abc"));

Predicate tooLong = s -> s.length() > 10;

if(tooLong.test("rensanning@gmail.com")){

System.out.println("long");

}

Consumer print = s -> System.out.println( s );

print.accept("foobar");

Supplier sup = () -> String.join("", Collections.nCopies(100, "a"));

System.out.println( sup.get() );

100620966.png

方法引用(Method References)

就是已存在方法的函数式接口,可以看做是调用一个方法的lambda简化。

比如:

lambda表达式: (Apple a) -> a.getWeight()

方法引用:Apple::getWeight

都代表了Apple类中的getWeight()方法

可以看得出这是lambda调用单个方法的语法糖。

其他的还有:

1个参数1个返回值:Math::pow 等价于 (x, y) -> Math.pow(x, y)

只有参数: System.out::println 等价于  x -> System.out.println(x)

2个参数1个返回值:String::compareToIgnoreCase 等价于  (x, y) -> x.compareToIgnoreCase(y)

等等。

4种类型

(1)构造函数

ClassName::new 比如:Integer::new

Function cFunc = Integer::new;

System.out.println(cFunc.apply("1")); // 1

(2)静态方法

ClassName::staticMethodName 比如:String::valueOf

Function sFunc = String::valueOf;

System.out.println(sFunc.apply(123)); // 123

(3)实例方法(类型)

ClassName::instanceMethodName 比如:String::length

Function func1 = String::length;

System.out.println(func1.apply("Java 8")); // 6

(4)实例方法(对象)

object::instanceMethodName 比如:obj:getValue

class Something {

String startsWith(String s) {

return String.valueOf(s.charAt(0));

}

}

Something something = new Something();

Function converter = something::startsWith;

System.out.println(converter.apply("Java")); // "J"

***在类的内部可以使用this引用当前类的方法,或者使用super引用父类的方法。比如:this::startsWith

class Base {

String endWith(String s) {

return String.valueOf(s.charAt(s.length() - 1));

}

}

class Something extends Base {

String startsWith(String s) {

return String.valueOf(s.charAt(0));

}

void test() {

Function converter1 = this::startsWith;

System.out.println(converter1.apply("abcdefg")); // a

Function converter2 = super::endWith;

System.out.println(converter2.apply("abcdefg")); // g

}

}

***类的静态成员变量的方法也可以引用。ClassA.StaticField::methodName 比如: System.out::println

class SomeElse {

public static final Something st = new Something();

}

Function p = SomeElse.st::startsWith;

System.out.println(p.apply("123")); // 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值