赞
踩
原创文章&经验总结&从校招到A厂一路阳光一路沧桑
详情请戳www.coderccc.com
对于Java开发者来说,Java8的版本显然是一个具有里程碑意义的版本,蕴含了许多令人激动的新特性,如果能利用好这些新特性,能够大大提升我们的开发效率。Java8的函数式编程能够大大减少代码量和便于维护,同时,还有一些跟并发相关的功能。开发中常用到的新特性如下:
在Java8之前,接口中只能包含抽象方法。那么这有什么样弊端呢?比如,想再Collection接口中添加一个spliterator抽象方法,那么也就意味着之前所有实现Collection接口的实现类,都要重新实现spliterator这个方法才行。而接口的默认方法就是为了解决接口的修改与接口实现类不兼容的问题,作为代码向前兼容的一个方法。
那么如何在接口中定义一个默认方法呢?来看下JDK中Collection中如何定义spliterator方法的:
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
可以看到定义接口的默认方法是通过default关键字。因此,在Java8中接口能够包含抽象方法外还能够包含若干个默认方法(即有完整逻辑的实例方法)。
public interface IAnimal { default void breath(){ System.out.println("breath!"); }; } public class DefaultMethodTest implements IAnimal { public static void main(String[] args) { DefaultMethodTest defaultMethod = new DefaultMethodTest(); defaultMethod.breath(); } } 输出结果为:breath!
可以看出IAnimal接口中有由default定义的默认方法后,那么其实现类DefaultMethodTest也同样能够拥有实例方法breath。但是如果一个类继承多个接口,多个接口中有相同的方法就会产生冲突该如何解决?实际上默认方法的改进,使得java类能够拥有类似多继承的能力,即一个对象实例,将拥有多个接口的实例方法,自然而然也会存在方法重复冲突的问题。
下面来看一个例子:
public interface IDonkey{ default void run() { System.out.println("IDonkey run"); } } public interface IHorse { default void run(){ System.out.println("Horse run"); } } public class DefaultMethodTest implements IDonkey,IHorse { public static void main(String[] args) { DefaultMethodTest defaultMethod = new DefaultMethodTest(); defaultMethod.breath(); } }
定义两个接口:IDonkey和IHorse,这两个接口中都有相同的run方法。DefaultMethodTest实现了这两个接口,由于这两个接口有相同的方法,因此就会产生冲突,不知道以哪个接口中的run方法为准,编译会出错:inherits unrelated defaults for run.....
解决方法
针对由默认方法引起的方法冲突问题,只有通过重写冲突方法,并方法绑定的方式,指定以哪个接口中的默认方法为准。
public class DefaultMethodTest implements IAnimal,IDonkey,IHorse {
public static void main(String[] args) {
DefaultMethodTest defaultMethod = new DefaultMethodTest();
defaultMethod.run();
}
@Override
public void run() {
IHorse.super.run();
}
}
DefaultMethodTest重写了run方法,并通过 IHorse.super.run();
指定以IHorse中的run方法为准。
静态方法
在Java8中还有一个特性就是,接口中还可以声明静态方法,如下例:
public interface IAnimal {
default void breath(){
System.out.println("breath!");
}
static void run(){}
}
函数式接口
Java8最大的变化是引入了函数式思想,也就是说函数可以作为另一个函数的参数。函数式接口,要求接口中有且仅有一个抽象方法,因此经常使用的Runnable,Callable接口就是典型的函数式接口。可以使用@FunctionalInterface
注解,声明一个接口是函数式接口。如果一个接口满足函数式接口的定义,会默认转换成函数式接口。但是,最好是使用@FunctionalInterface
注解显式声明。这是因为函数式接口比较脆弱,如果开发人员无意间新增了其他方法,就破坏了函数式接口的要求,如果使用注解@FunctionalInterface
,开发人员就会知道当前接口是函数式接口,就不会无意间破坏该接口。下面举一个例子:
@java.lang.FunctionalInterface
public interface FunctionalInterface {
void handle();
}
该接口只有一个抽象方法,并且使用注解显式声明。但是,函数式接口要求只有一个抽象方法却可以拥有若干个默认方法的(实例方法),比如下例:
@java.lang.FunctionalInterface
public interface FunctionalInterface {
void handle();
default void run() {
System.out.println("run");
}
}
该接口中,除了有抽象方法handle外,还有默认方法(实例方法)run。另外,任何被Object实现的方法都不能当做是抽象方法。
lambda表达式
lambda表达式是函数式编程的核心,lambda表达式即匿名函数,是一段没有函数名的函数体,可以作为参数直接传递给相关的调用者。lambda表达式极大的增加了Java语言的表达能力。lambda的语法结构为:
(parameters) -> expression
或
(parameters) ->{ statements; }
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
完整示例为(摘自菜鸟)
public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明 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); } }
另外,lambda还可以访问外部局部变量,如下例所示:
int adder = 5;
Arrays.asList(1, 2, 3, 4, 5).forEach(e -> System.out.println(e + adder));
实际上在lambda中访问类的成员变量或者局部变量时,会隐式转换成final类型变量,所以上例实际上等价于:
final int adder = 5;
Arrays.asList(1, 2, 3, 4, 5).forEach(e -> System.out.println(e + adder));
方法引用是为了进一步简化lambda表达式,通过类名或者实例名与方法名的组合来直接访问到类或者实例已经存在的方法或者构造方法。方法引用使用**::来定义,:声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。