接口以及函数式接口(JDK8新特性)

引言:

JDK8之前在接口中添加新的方法就需要修改它的实现类
因此为了兼容老的接口JDK8就增加了接口新特性

在JDK8之前接口只能定义抽象方法,它的默认修饰符是public abstract

//JDK8之前的写法
interface MyInterface1{
        void eat();
        void sleep();
        void play();        
//此时等效于 public abstract void eat();
}

JDK8接口主要增加了默认方法静态方法以及四个函数式接口

默认方法:

默认方法可以有方法体,可以被实现类使用

注意1: 默认方法必须用default修饰
默认方法格式:
			default   返回值类型   方法名(参数列表){
                             //方法体语句
             }

如果不用default就会默认public abstract修饰,此时不能有方法体!

//定义一个接口
interface Myface{
	public abstract void eat();  //抽象方法
	//这里定义一个默认方法
	default void fun() {
		System.out.println("接口中的默认方法");
	}
}
//定义一个实现类
class Test implements Myface{
     //抽象方法依旧需要重写,先重写eat方法
     //实现了某接口,就拥有了某接口的默认方法
     @Override
     public void eat(){
     System.out.println("实现接口中的方法");
	}    
}
//测试类
class Test1{
     public static void main(String[] arg){
        Test t = new Test();
          t.eat();
        //使用接口中的默认方法
         t.fun();
}
//输出结果:
//实现接口中的方法
//接口中的默认方法
}

注意2:重写默认方法时权限修饰符必须是public

借用以上代码

//定义一个接口
interface Myface{
	public abstract void eat();  //抽象方法
	//这里定义一个默认方法
	default void fun() {
		System.out.println("接口中的默认方法");
	}
}
//定义一个实现类
class Test implements Myface{
     //抽象方法依旧需要重写,先重写eat方法
     @Override
     public void eat(){
     System.out.println("实现接口中的方法");
	}
	//重写接口中的fun方法
	@Override
	public void fun(){
	System.out.println("重写接口中的默认方法");
    }    
}
//测试类
class Test1{
     public static void main(String[] arg){
        Test t = new Test();
          t.eat();
        //使用接口中的默认方法
        t.fun();
}

此时引入一个问题?如果一个类实现两个接口,而这两个接口中有相同的默认方法,此时会发生什么?

因为在JDK8之前我们的接口都是抽象方法,即使出现相同的方法名也无关紧要,因为他们都没有方法体,实现类重写抽象方法也不会造成歧义性,而现在我们应该怎么做呢?

实现多个接口时如果有相同方法签名的默认方法,必须对该默认方法进行重写

//定义第一个接口,包含show方法
interface Myface1{
	default void show() {
		System.out.println("这是Myface1的show方法");
	}
}
//定义第二个接口,包含show方法
interface Myface2{
	default void show() {
		System.out.println("这是Myface2的show方法");
	}
}
//定义一个实现类,此时的实现类必须重写show方法。
class Test1 implements Myface1,Myface2{
   //没有重写的话会报错,不重写不知道该实现哪个方法
	@Override
	public void show() {		
		System.out.println("重写两个接口中的show方法");
	}
}
//测试类
class Test{
public static void main(String[] arg){
	Test1 tt = new Test1();
	tt.show();
}
}
上边的案例我们可以看到,实现类必须重写两个接口中相同的方法,
那么我们怎么使用两个接口中的方法呢?
基本格式: 接口名.super.方法名
这里使用上述代码,只对实现类进行修改
class Test1 implements Myface1,Myface2{
   //没有重写的话会报错,不重写不知道该实现哪个方法
	@Override
	public void show() {		
		System.out.println("重写两个接口中的show方法");
	}
	//定义一个方法去调用接口中的方法
	public void show2(){
	   Myface1.super.show();  //调用Myface1接口中的show方法
	   Myface2.super.show();  //调用Myface2接口中的show方法
}
}

默认方法注意:

1.父接口默认方法可以直接被实现类所使用,不强制重写(实现单接口,或者是多接口没有相同的默认方法)
2.实现类重写接口中的方法,权限修饰符必须是public 
3.重写父接口中默认方法时如需要调用接口中的默认方法可以使用:接口名.super.方法名(参数列表);
4.如果一个类继承一个父类的同时实现多个接口,原则是:"父优先"

静态方法:

JDK8还可以在接口中定义静态方法

格式:
		static  返回值类型   方法名(参数列表){
             //方法体
     }

静态方法的使用比较简单,有个注意事项

父接口中的静态方法不能被实现类所直接使用,只能通过:接口名.直接调用

代码演示:

interface MyJieKou{
	//定义一个静态方法
	static void run(){
	System.out.println("接口中的静态方法执行");
}
}
//定义一个实现类
class TestJieKou implements MyJieKou{
   //这里类不拥有接口中的静态方法
}
//测试类
class Test{
public static void main(String[] args){
TestJieKou t1 = new TestJieKou();
//此时不能使用 t1.run()
//如果需要接口中的静态方法,则可以直接调用
     MyJieKou.run();
}
}

函数式接口:

在函数式接口的学习前如果你还不会使用Lambda表达式,那么你很有必要先去了解一下我的另一篇文章:Lambda表达式入门

前言:为什么要引入函数式接口?

我的理解是,为了满足不同用户对同一方法的不同要求(这里不是重载的理念)

我们知道方法的参数列表是一些数据类型,而有时我们还需要一些方法作为参数,然而方法又不属于数据类型,那么我们怎么来实现这个操作呢?

函数式接口是JDK1.8引入的新概念,我们学习函数式接口一般会学习四个,他们分别是:

1.消费型接口:
   Consumer<T>
      void accept(T t) : 对于T类型数据的处理方式
2.供给型接口:
   Supplier<T>
      T get(): 获取到T类型数据
3.函数型接口:
   Function<T,R>
      R apply(T) : 通过T类型参数, 获取到R类型数据结果
4.断言型接口 :
   Predicate<T>
       boolean test(T) : 提供一个T类型参数,判断出T类型参数是否符合规则和要求

接下来我们采用具体的案例来分别进行演示:

消费型接口

Consumer< T >

接受输入单个参数,不返回结果的操作

Consumer<T>   来自:java.util.function
方法:
         accept(T t) :对给定的参数执行此操作
    
       解释:如果有这么一个参数t,我们需要对它进行处理,
  可处理的方式很多,我们不能确定如何操作,就可以使用消费型接口作为
  参数,对传入的参数进行不同的处理,根据用户自己的需求去实现

案例分析:

需求 : 定义出一个方法功能, 客户消费指定金额, 这些金额都如何消费的
客户1 : 花了500元, 买了一把大宝剑
客户2 : 花了400元, 买了一双小花猫
… 还有很多很多的客户, 对于指定金额有不同的消费

那么用户这么多,需求又不一样,我们怎么定义这个方法呢,此时我们的函数式接口就可以很好的实现

代码分析:

import java.util.function.Consumer;
//使用的时候需要导包哦!
public class Test_消费型接口 {
	public static void main(String[] args) {
	   //用户1
		Consumer<Double> c1 = (x) -> System.out.println("花费"+x+"买一把大宝剑");
		buy(500,c1);
		//用户2,还可以对钱进行判断,执行相应的操作
		Consumer<Double> c2 = (x) ->{
			if(x > 300) {
				System.out.println("太贵了,先不买吧");
			}else {
				System.out.println("花费"+x+"买一只小花猫");
			}		
		};
		buy(100,c2);
//输出结果:
//花费500.0买一把大宝剑
//花费100.0买一只小花猫
	}	
	//定义一个方法去实现不同的用户完成不同的需求
//注意,泛型只能使用引用数据类型,所以要使用基本数据类型的包装类
	public static void buy(double money,Consumer<Double> con) {
//接受同一个参数,执行不同的方法
		con.accept(money);	
	}
}

供给型接口

Supplier< T >

不接受参数,返回需要的结果

 Supplier< T >    来自 java.util.function
 方法:       T  get()   //返回用户指定的类型
 
 获取T类型的结果,却不知道怎么获取

案例分析:

需求 : 定义出一个方法功能, 方法能给客户返回一个ArrayList容器, 返回的容器中包含几个数据, 集合中存储的数据有什么规律, 根据客户的实际要求决定
客户1 : 存储5个数据, 数据是30-80之间的随机数
客户2 : 存储8个数据, 数据是1-100之间的随机偶数

每一个客户都需要得到符合条件的集合容器

代码分析:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
public class Test_供给型接口 {
	public static void main(String[] args) {
//定义用户1需要获取的方式
		Supplier<Integer> sup1 = () ->{
			Random ran = new Random();
			int i = ran.nextInt(51)+30;
			return i;
		};
		System.out.println(getList(5,sup1));
//定义用户2需要获取的方式
		Supplier<Integer> sup2 = () ->{
			Random ran = new Random();
			while(true) {				
				int i = ran.nextInt(100)+1;
				if(i % 2 == 0) {
					return i;
				}
			}
		};
		System.out.println(getList(8,sup2));
//输出结果:
// [71, 60, 66, 64, 41]
// [66, 82, 10, 12, 76, 58, 56, 52]		
	}
//定义一个用户需要获取的方法	
	public static List<Integer> getList(int n,Supplier<Integer> sup){
		List<Integer> list = new ArrayList<>();
		for(int i = 0; i < n; i++) {
//get()方法来返回用户需要的结果
			list.add(sup.get());
		}	
		return list;
	}

}

函数型接口

Function < T, R>
Function<T,R>  来自 java.util.function
方法:        R  apply(T t)   将此函数应用于指定的参数
      解释:如果方法中已经有数据T类型,想通过T类型的参数,获得R类型的结果,具体怎么通过T计算出R类型的,方式很多,不能具体确定,那么就可以将Function接口作为方法参数传递,相当于传递apply();

需求 : 定义出一个方法功能, 根据int类型x的值, 计算出另外一个int类型y的值, y获取方式根据客户的要求决定
客户1 : y值为x的2倍
客户2 : y值为x + 1
客户3 : y值为x-1

代码分析:

import java.util.function.Function;

public class Test_函数式接口 {
	public static void main(String[] args) {
	//这里定义三个函数,第一个参数是传入的数据类型,第二个参数是需求的返回值的数据类型
		Function<Integer,Integer> f1 = (x) -> 2*x;
        Function<Integer,Integer> f2 = (x) ->x+1;
        Function<Integer,Integer> f3 = (x) -> x-1;
		System.out.println(get(10,f1));
		System.out.println(get(10,f2));
		System.out.println(get(10,f3));
		
	}
//定义方法来传递不同的参数返回用户需求的方法
	public static int get(int x,Function<Integer,Integer> fun) {
		return fun.apply(x);
	}
}

断言型接口

Predicate < T >
Predicate<T>  来自 java.util.function
方法:     boolean test(T)   在给定的参数上评估这个谓词
解释:如果方法中有T类型数据,需要判断出T类型参数是否符合规则
和要求,返回boolean类型结果,可以将Predicate断言型接口作为
方法参数传递,相当于传递test方法功能
 

需求 : 万能的数据筛选功能; 客户提供一个ArrayList容器, 根据客户的要求, 将容器中符合条件的数据筛选出来, 放置到一个新的ArrayList容器给客户, 筛选规则由客户决定
客户1 : 筛选出集合中所有小于100的偶数
客户2 : 筛选出集合中所有的奇数
客户3 : 筛选出集合中所有大于50的数

代码分析:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Test_断言型接口 {
	public static void main(String[] args) {
		ArrayList<Integer> list2 = new ArrayList<>();
		list2.add(120);
		list2.add(43);
		list2.add(68);
		list2.add(98);
		list2.add(23);
		list2.add(76);
		//创建一个集合
    	Predicate<Integer> p1 = (x) -> x > 100 && x%2 ==0;
    	System.out.println(panduan(list2,p1));
    	Predicate<Integer> p2 = (x) -> x % 2 != 0;
    	System.out.println(panduan(list2,p2));
    	Predicate<Integer> p3 = (x) -> x > 50;
    	System.out.println(panduan(list2,p3));
		
	}
//传递一个集合,返回筛选后的集合
  public static List<Integer> panduan(ArrayList<Integer> list,Predicate<Integer> pre){
	  ArrayList<Integer> li = new ArrayList<>();
	  for(int i = 0; i < list.size(); i++) {
		  int len = list.get(i);
//断言型返回一个boolean类型
		  if(pre.test(len)) {
			  li.add(len);
		  }
	  } 
	  return li;  
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值