Java8新特性总结-接口与Lambda表达式

Java8新特性小结-接口与Lambda表达式

Java8的新特性相对于前版本(Java7)来说,主要体现在两个方面:

1.   接口定义与使用

2.   Lambda表达式对匿名内部类的简化使用。

 

Java8新特性的具体表现如下:

1.在接口中的体现

(1)在接口中可以定义实体方法,但除原先的抽象方法外只能定义两种方法:

A.公共的静态方法

如:

package com.jasberyon.java8.interfacer;

public interface InterfaceA {

	public static void sayHi(){
		System.out.println("InterfaceA---sayHi");
	}
}

需要注意的是:

a.由于静态方法是属于类(文件)的,所以调用时需要使用对应的接口(静态方法所在类)名去调用。所以在Java8中调用接口中静态方法时,只能通过接口名去调用,使用实现类是无法调用的。


B.使用default关键字声明的普通方法

如:

<span style="font-size:12px;">package com.jasberyon.java8.interfacer;

public interface InterfaceA {
	
	public default void sayHi2(){
		System.out.println("InterfaceA---sayHi2");
	}
}
</span>

需要注意的是:

b.default关键字只能在接口中使用。那么当在接口的具体实现类中重写该default关键之标识的方法时就需要去掉default关键字。如:

package com.jasberyon.java8.interfacer;

public class InterfaceImpl implements InterfaceA {

	public static void main(String[] args){
		InterfaceA.sayHi();
		InterfaceImpl.sayHi();
	}
	
	public static void sayHi(){
		System.out.println("InterfaceImpl---sayHi");
	}
	
	public void sayHi2(){
		System.out.println("InterfaceB---sayHi2");
	}
}

在上面的代码情况(重写接口中的default关键字标识的方法)下当多态调用时,同样会走现实类(子类)的方法,如果没有重写,则走接口中的default方法。

例如:

package com.jasberyon.java8.interfacer;

public class InterfaceImpl implements InterfaceA {

	public static void main(String[] args){
		InterfaceA.sayHi();
		InterfaceImpl.sayHi();
		InterfaceA ia = new InterfaceImpl();
		ia.sayHi2();
	}
	
	public static void sayHi(){
		System.out.println("InterfaceImpl---sayHi");
	}
	
	public void sayHi2(){
		System.out.println("InterfaceB---sayHi2");
	}
}

输出结果:

InterfaceA---sayHi

InterfaceImpl---sayHi

InterfaceB---sayHi2

注意: 因为在Java中类是单继承的,而接口却是可以多实现的。这样这几的初衷是出于安全性的考虑。因为在多继承的模式中,如果子类C继承了父类A和B,而A和B中又有相同的方法methodAlike。那么这时就无法区分子类C中使用方法时到底是使用哪一父类中的方法了。而(原先的设计)接口则不同,实现类需要实现接口中定义的方法,则不存在上述的安全性问题。

 

那么,现在的问题是:由于在Java8的新特性中可以在接口中定义非静态的方法,那么当多个接口中定义了相同的非静态default方法时,如果实现类实现了这多个接口时,是不是就出现了多继承了呢?

答案是否定的

 

这时就会产生编译时错误,需要在实现类中覆盖相同的接口中定义的所有方法方法。

 

如:

定义接口InterfaceA

package com.jasberyon.java8.interfacer;

public interface InterfaceA {

	public static void sayHi(){
		System.out.println("InterfaceA---sayHi");
	}
	
	public default void sayHi2(){
		System.out.println("InterfaceA---sayHi2");
	}
}

定义接口InterfaceB

package com.jasberyon.java8.interfacer;

public interface InterfaceB {

	public static void sayHi(){
		System.out.println("InterfaceB---sayHi");
	}
	
	public default void sayHi2(){
		System.out.println("InterfaceB---sayHi2");
	}
}

那么此时就会发现接口InterfaceA和InterfaceB中有相同的方法:public defaultvoidsayHi2(),那么此时实现类就必须去重写相同的方法。

 

实现类InterfaceImpl

package com.jasberyon.java8.interfacer;

public class InterfaceImpl implements InterfaceA, InterfaceB {

	public static void sayHi(){
		System.out.println("InterfaceImpl---sayHi");
	}
	
	public void sayHi2(){
		System.out.println("InterfaceB---sayHi2");
	}
}

小结:Java的接口本身就是一种为扩展程序而使用的,在Java8中应该避免让实现类去覆盖具有多个相同default方法的接口,这样没有什么意义。Java8在接口中的新特性仅作为一个扩展而使用。而对于接口中的静态方法,在实现类中去“重写”(实际上不是)是没有意义的。


2. Lambda表达式对匿名内部类的简化使用

Lambda表达式替换了原有的匿名内部类的写法,简化了匿名内部类的使用方式。当然简化的方式总是会使得功能使用时有所限制,Just like 增强型for(for-each)循环。

 

Lambda表达式的语法结构:

(参数1,参数2...)->{

    重写方法内容,不写定义方法名。

}


(1)多线程时使用Lambda表达式


原来的使用匿名内部类实现多线程的栗子:

package com.jasberyon.java8.lambda;

public class ThreadDemo {

	public static void main(String[] args) {
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i=0; i<200; i++){
					System.out.println("执行run---------"+i);
				}
				
			}
		}).start();
			
		for(int j=0; j<200; j++){
			System.out.println("执行mian---------------"+j);
		}
		
	}
}

使用Lambda表达式改造后:

package com.jasberyon.java8.lambda;

public class ThreadDemo {

	public static void main(String[] args) {
		
		Runnable runnable = ()->{
			for(int i=0; i<200; i++){
				System.out.println("执行run---------"+i);
			}
		};
		new Thread(runnable).start();
		
		for(int j=0; j<200; j++){
			System.out.println("执行mian---------------"+j);
		}	
		
	}
	
}

又或者这样写:

package com.jasberyon.java8.lambda;

public class ThreadDemo {

	public static void main(String[] args) {
		
		new Thread(()->{
			for(int i=0; i<200; i++){
				System.out.println("执行run---------"+i);
			}
		}).start();
		
		for(int j=0; j<200; j++){
			System.out.println("执行mian---------------"+j);
		}
	}
	
}

那么,Lambda的弊端也是显而易见的,如果接口中定义了多个抽象方法,那么就只能使用传统方式了。


(2)集合排序“比较器”中使用Lambda表达式

 

TreeSet会将字符串按照自然顺序进行排序,如下代码:

package com.jasberyon.java8.lambda;

import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>();
		set.add("asdafa");
		set.add("abcdefsadf");
		set.add("sahdfoad");
		set.add("bhsayuadasdfasdf");
		set.add("auiweyqwergeawgfasdasd");
		System.out.println(set);
	}

}

结果输出:

[abcdefsadf, asdafa,auiweyqwergeawgfasdasd, bhsayuadasdfasdf, sahdfoad]


使用自定义的比较器后:

package com.jasberyon.java8.lambda;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>(new MyCompareMethod());
		set.add("asdafa");
		set.add("abcdefsadf");
		set.add("sahdfoad");
		set.add("bhsayuadasdfasdf");
		set.add("auiweyqwergeawgfasdasd");
		System.out.println(set);
	}

}

class MyCompareMethod implements Comparator<String>{

	@Override
	public int compare(String o1, String o2) {
		int length = o1.length() - o2.length();
		return length == 0?o1.compareTo(o2):length;
	}
	
}

结果输出:

[asdafa, sahdfoad, abcdefsadf,bhsayuadasdfasdf, auiweyqwergeawgfasdasd]


使用匿名内部类后:

package com.jasberyon.java8.lambda;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>(new Comparator<String>(){
			@Override
			public int compare(String o1, String o2) {
				int length = o1.length() - o2.length();
				return length == 0?o1.compareTo(o2):length;
			}
		});
		set.add("asdafa");
		set.add("abcdefsadf");
		set.add("sahdfoad");
		set.add("bhsayuadasdfasdf");
		set.add("auiweyqwergeawgfasdasd");
		System.out.println(set);
	}

}

使用Lambda表达式改造:

package com.jasberyon.java8.lambda;

import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>((String o1, String o2)->{
			int length = o1.length() - o2.length();
			return length == 0?o1.compareTo(o2):length;
		});
		set.add("asdafa");
		set.add("abcdefsadf");
		set.add("sahdfoad");
		set.add("bhsayuadasdfasdf");
		set.add("auiweyqwergeawgfasdasd");
		System.out.println(set);
	}
	
}

也可写成:

package com.jasberyon.java8.lambda;

import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>((o1, o2)->{
			int length = o1.length() - o2.length();
			return length == 0?o1.compareTo(o2):length;
		});
		set.add("asdafa");
		set.add("abcdefsadf");
		set.add("sahdfoad");
		set.add("bhsayuadasdfasdf");
		set.add("auiweyqwergeawgfasdasd");
		System.out.println(set);
	}
	
}

值得注意的是,此时需要泛型标注。也就是比较的类型必须在<>中声明,否则无法通过编译。

 

同时,使用Lambda表达式的实现不会再有额外的类文件(原来的方式匿名内部类也是要产生形如Ttt$xx.class的文件的)产生。

 

其它的还有很多就不在列举了。记住一点,构造匿名内部类时,如果只有一个方法需要重写,那么就可以使用Lambda表达式。


版权声明:本文为博主原创文章,未经博主允许不得转载。