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

[size=medium][b][color=blue]默认方法(Default Methods)[/color][/b][/size]
在Java的interface中一直只是包含方法的定义不能有实现,而且Java也不支持多继承。参考[url=http://rensanning.iteye.com/blog/1847732]Why Multiple Inheritance is Not Supported in Java[/url]。Java 8开始提供在interface中可以有方法的实现,这个特性叫默认方法“Default Methods”。如此以来,interface有了方法实现,Java即可实现多继承,Java一贯提倡不支持多继承,为什么这次妥协了呢?

因为interface太过依赖于他们的实现类了,要往interface中添加一个方法就必须修改所有它的实现类。Java8的Lambda表达式基本上都是一个函数式接口,为了实现lambda表达式并无缝支持Lambda表达式,Java核心的一些类就需要修改。但是像java.util.List这样的接口不仅仅在JDK中被实现,还有其他很多第三方库在实现,稍微的改动都存在兼容性问题。试想一下如果没有默认方法的话,是不是要修改所有集合类的实现啊。
List<?> list = …
list.forEach(…);

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

[b]简单试用:[/b]
interface Person {
// adds a java 8 default method
default void sayHello() {
System.out.println("Hello");
}
}
class Sam implements Person {
}

new Sam().sayHello();


[b]多重继承:[/b]
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();


[b]调用继承过来的默认方法重名的时候:[/b]
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();
}
}


[b]抽象方法和默认方法重名的时候:[/b]
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


[b]默认方法再抽象化:[/b]
public class D1 {

public static void main(String[] args) {
// 默认方法再抽象化
new D1().test(); // FOO2
}

interface Foo {
default void print() {
System.out.println("FOO");
}
}

interface Foo2 extends Foo {
@Override
void print();
}

public void test() {
Foo2 foo = new Foo2() {
@Override
public void print() {
System.out.println("FOO2");
}
};

foo.print();
}
}


[b]多重继承相同的默认方法必须覆写:[/b]
public class D2 {

public static void main(String[] args) {
// 多重继承相同的默认方法必须覆写
new D2().new FooBar().print(); // FOOBAR
}

interface Foo {
default void print() {
System.out.println("FOO");
}
}

interface Bar {
default void print() {
System.out.println("BAR");
}
}

class FooBar implements Foo, Bar {
@Override
public void print() {
System.out.println("FOOBAR");
}
}

}


[b]父类优先于接口:[/b]
public class D3 {

public static void main(String[] args) {
// 父类优先于接口
new D3().test(); // BAR
}

interface Foo {
default void print() {
System.out.println("FOO");
}
}

class Bar {
public void print() {
System.out.println("BAR");
}
}

class FooBar extends Bar implements Foo {
}

public void test() {
FooBar foobar = new FooBar();
foobar.print();
}

}


默认方法不支持final和synchronized修饰符,只支持public, abstract, default, static, strictfp。
interface NoTrait {
default final void noFinal() {
// ^^^^^ modifier final not allowed
System.out.println("noFinal");
}
default synchronized void noSynchronized() {
// ^^^^^^^^^^^^ modifier synchronized not allowed
System.out.println("noSynchronized");
}
}


除过以前的那些区别外,带有默认方法的interface和抽象类的区别:
[list][*]抽象类是有个状态的,它可以有构造函数和成员变量
[*]抽象类不能用于lambda表达式中[/list]

[color=red]默认方法不能覆盖 equals, hashCode, toString 方法!!![/color]
原因可以参考Brian Goetz给出的回答:[url=http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html]Allow default methods to override Object's methods[/url]

[url=http://viralpatel.net/blogs/java-8-default-methods-tutorial/]http://viralpatel.net/blogs/java-8-default-methods-tutorial/[/url]
[url=http://zeroturnaround.com/rebellabs/java-8-explained-default-methods/]http://zeroturnaround.com/rebellabs/java-8-explained-default-methods/[/url]

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

[b]定义[/b]
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();
}
}


[b]实现[/b]

// 匿名类实现
new MyFi1() {
@Override
public void run() {
System.out.println("Anonymous Inner class");
}
}.run();


// lambda表达式实现只需要提供形式参数和方法体。
// 函数式接口只有一个抽象方法,编译器能自动推断方法。
MyFi1 dbl = () -> System.out.println("Lambda Expression");
dbl.run();

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

public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

[color=red]Comparator接口里面虽然声明了两个方法,但它是函数接口。因为equals方法是Object的,不影响它依然是个函数接口。[/color]

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

[b]分类[/b]
[list]
[*]函数:Function / BiFunction  T -> R
[*]运算:UnaryOperator / BinaryOperator T -> T 或 (T, T) -> T
[*]预言:Predicate / BiPredicate
[*]生产者:Supplier 返回T
[*]消费者:Consumer / BiConsumer 只有一个参数
[/list]

[b]最常用的:[/b]
[list]
[*]1个参数一个返回值:Function<paramType, returnType>
[*]1个参数没有返回值:Consumer<paramType>
[*]没有参数一个返回值:Supplier<returnType>
[/list]

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

Function<String, Integer> len = s -> s.length();
System.out.println( len.apply("abc"));

Predicate<String> tooLong = s -> s.length() > 10;
if(tooLong.test("rensanning@gmail.com")){
System.out.println("long");
}

Consumer<String> print = s -> System.out.println( s );
print.accept("foobar");

Supplier<String> sup = () -> String.join("", Collections.nCopies(100, "a"));
System.out.println( sup.get() );


[img]http://dl2.iteye.com/upload/attachment/0095/3996/002e7ce9-d609-3d9a-b294-182d7f42b12e.png[/img]

[size=medium][color=blue][b]方法引用(Method References)[/b][/color][/size]
就是已存在方法的函数式接口,可以看做是调用一个方法的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)
等等。

[b]4种类型[/b]

[b](1)构造函数[/b]
ClassName::new 比如:Integer::new
Function<String, Integer> cFunc = Integer::new;
System.out.println(cFunc.apply("1")); // 1


数组的构造函数:[b]String[]::new[/b]

[b](2)静态方法[/b]
ClassName::staticMethodName 比如:String::valueOf
Function<Integer, String> sFunc = String::valueOf;
System.out.println(sFunc.apply(123)); // 123


[b](3)实例方法(类型)[/b]
ClassName::instanceMethodName 比如:String::length
Function<String, Integer> func1 = String::length;
System.out.println(func1.apply("Java 8")); // 6


[b](4)实例方法(对象)[/b]
object::instanceMethodName 比如:obj:getValue

class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Function<String, String> 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<String, String> converter1 = this::startsWith;
System.out.println(converter1.apply("abcdefg")); // a
Function<String, String> 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<String, String> p = SomeElse.st::startsWith;
System.out.println(p.apply("123")); // 1


***抛出异常的方法不能用于方法引用
List<Path> paths = null; 

// Unhandled exception type IOException
paths.forEach(Files::delete); //NG

paths.forEach(path -> {
try {
Files.delete(path);
} catch (IOException ex) {
// 例外処理
}
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值