`
rensanning
  • 浏览: 3513673 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:37477
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:604281
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:678011
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:87236
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:399799
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69058
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:90460
社区版块
存档分类
最新评论

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

    博客分类:
  • Java
阅读更多
默认方法(Default Methods)
在Java的interface中一直只是包含方法的定义不能有实现,而且Java也不支持多继承。参考Why Multiple Inheritance is Not Supported in Java。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#了。

简单试用:
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


默认方法再抽象化:
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();
	}
}


多重继承相同的默认方法必须覆写:
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");
		}
	}

}


父类优先于接口:
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和抽象类的区别:
  • 抽象类是有个状态的,它可以有构造函数和成员变量
  • 抽象类不能用于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”。

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

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

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

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


最常用的:
  • 1个参数一个返回值:Function<paramType, returnType>
  • 1个参数没有返回值:Consumer<paramType>
  • 没有参数一个返回值:Supplier<returnType>


对于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() );




方法引用(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<String, Integer> cFunc = Integer::new;
System.out.println(cFunc.apply("1")); // 1


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

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


(3)实例方法(类型)
ClassName::instanceMethodName 比如:String::length
Function<String, Integer> 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<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) {
        // 例外処理
    }
});
  • 大小: 72.8 KB
分享到:
评论

相关推荐

    Java 8 简明教程 (Java 8 Tutorial中文版)文字pdf版

    Java 8 简明教程 Java 8 Tutorial中文版 “Java并没有没落,人们很快就会发现这一点” 欢迎阅读我编写的Java 8介绍。本教程将带领你一步一步地认识...了解,例如:流控制,函数式接口,map扩展和新的时间日期API等等。

    Java8新特性及实战视频教程完整版.txt

    函数式编程中中新加了一些概念:Lambda表达式、函数式接口、函数引用、默认方法、Optional类等;Stream中提供了一些流式处理集合的方法,并提供了一些归约、划分等类的方法;日期中添加了ZoneDateTime、DataFormat等...

    尚硅谷Java视频Java8新特性视频教程

    尚硅谷_Java8新特性_四大内置核心函数式接口 ·06. 尚硅谷_Java8新特性_方法引用与构造器引用 ·07. 尚硅谷_Java8新特性_创建 Stream ·08. 尚硅谷_Java8新特性_Stream_筛选与切片 ·09. 尚硅谷_Java8新特性_Stream...

    JDK(Java Development Kit)1.8是Java平台的一个版本,也被称为Java 8 下面是关于JDK 1.8

    函数式接口结合Lambda表达式和方法引用,使得开发人员能够将方法作为参数传递,提高了代码的灵活性和复用性。 3. 流式API(Stream API):JDK 1.8中引入了Stream API,它提供了一种处理集合数据的流式操作方式,...

    《Java 8 in Action》是一本关于 Java 编程语言的书籍,重点介绍了 Java 8 中引入的新特性和改进

    该书由 Mario Fusco、Alan Mycroft 和 Raoul-Gabriel Urma 合著,旨在帮助读者深入了解 Java 8,并掌握其中的关键...其他新特性: 简要介绍 Java 8 中引入的其他新特性,如接口的默认方法、方法引用、Optional 类型等。

    Java的lambda表达式讲解代码(各种lambda表达式使用方式:无参、一个参数、多参数、简化形式...)

    4、函数式接口使用:学习如何使用Lambda表达式与Java中的函数式接口进行交互,包括传递函数、使用函数式接口的默认方法和方法引用。 本源码资源旨在帮助用户掌握以下几个方面: 1、Lambda表达式概述:了解Lambda...

    Java8 简明教程.pdf

    Java8 简明教程.pdf 本教程将带领你一步一步地认识这门语言的新特性。通过简单明了的代码 示例,你将会学习到如何使用...你还将对最新推出的API有一定的了解,例如:流控制,函数式接口,map扩展和新的时间日期API等等

    Java8 新特性.pdf

    Java 8新特性PDF课件,介绍了Lambda表达式,函数式接口,方法引用,Stream API ,接口中的默认方法和静态方法,新日期时间API, Option接口等特性

    java8stream源码-jdk8:java8新特性

    如果一个接口只有一个抽象方法(其他的方法比如默认方法或与java.lang.Object类相同的方法可以有多个),那么该接口就是一个函数式接口 如果我们在某个接口上声明了FunctionInterface注解,那么编译器会按照函数式接口...

    JAVA - JDK 1.8 API 帮助文档-中文版

    本文档将Java8的新特新逐一添加,比如如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API上的改进,比如流,函数式接口,Map以及全新的日期API。祝你Java技术更上一层楼!

    win jdk-1.8 java 基础环境

    2、函数式接口 3、方法引用和构造引用 4、Stream API 5、接口中的默认方法和静态方法 6、新时间日期API 7、OPtional 8、其他特性 二、java8(JDK1.8)新特性简介 1、速度快; 2、代码少、简介(新增特性:...

    02.第二节-接口中可以定义默认普通方法.mp4

    即使是升级到JDK1.8了,但是很多开发者依然是停留在1.8之前的开发方式,使用的也是非常老旧和过时的API,遇到函数式接口也是依然使用匿名内部类解决。 本系列课程从Lambda表达式、方法的引用、Stream API三处着手...

    java8的新特性.doc

    自己整理的java8新特性及demo ...5. 四大函数式接口 8 6. Stream 11 7. Map接口的新方法: 19 8. 并行流与串行流 20 9. Optional容器类 23 10. 接口中默认的方法和静态方法 26 11. 日期 26

    mac os jdk8

    Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。 Date Time API − 加强对日期与时间的处理。 Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空...

    java8stream源码-Java-8-Reference:参考java8新特性

    java8流源码Java8 参考 这是我自己对 java 8 中的新结构和特性的引用 接口和 Lambda 表达式 ...函数式接口是只包含一个抽象方法的接口。 @FunctionalInterface interface IFuntionalInterface &lt;R

    写给大忙人看的JAVA SE 8

    1.3 函数式接口 6 1.4 方法引用 8 1.5 构造器引用 10 1.6 变量作用域 10 1.7 默认方法 14 1.8 接口中的静态方法 17 练习 18 第2章 Stream API 20 2.1 从迭代器到Stream操作 22 2.2 创建Stream 23 2.3 filter、map和...

    Java8十大特性详解.txt

    Java8 十大新特性详解 接口的默认方法详解、Lambda 表达式详解、函数式接口详解、方法与构造函数引用详解、Lambda 作用域详解、访问对象字段与静态变量详解、访问接口的默认方法详解

    java8源码-jdk8:Java8(又称为jdk1.8)是Java语言开发的一个主要版本。Oracle公司于2014年3月18日发布Java

    接口:从jdk8开始,接口可以定义为函数式接口,并新增了两种类型的方法,【静态方法和默认方法】打破了接口以前的定义“实现这个接口就一定要实现接口中的所有方法”,具体请看示例。 Stream API:新添加的Stream ...

Global site tag (gtag.js) - Google Analytics