java基础~函数式接口

这篇具有很好参考价值的文章主要介绍了java基础~函数式接口。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.简介

函数式接口在java中指:有且仅有一个抽象方法的接口

函数式接口,即适用于函数式编程场景的接口.而java中体现函数式编程的就是Lambda,所以函数式接口就是可以适用于lambda表达式的接口.只有确保接口中有且仅有一个抽象方法,java中的lambda表达式才能顺利地推导

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能

注:语法糖就是值使用更加方便,但是原理不变的代码语法,例如遍历集合时候使用的for-each语法,其实底层原理仍然人是迭代器,这便是语法糖.从应用层面来讲,java中的Lambda表达式可以当做匿名内部类的语法糖,但是二者在原理上是不同的

@FunctionalInterface注解 
与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上

通过JDK8源码javadoc,可以知道这个注解有以下特点:
	1、该注解只能标记在"有且仅有一个抽象方法"的接口上。
	2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
	3、接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。	
	4、该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让	编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

2.函数式接口

2.1Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据
public class SupplierDemo {
    private static String getString(Supplier<String> function) {
      	return function.get();
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        System.out.println(getString(() -> msgA + msgB));
    }
}

2.2 Consumer接口

java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
public class ConsumerDemo {
    private static void consumeString(Consumer<String> one, Consumer<String> two,String s) {
         one.andThen(two).accept(s);
    }

    public static void main(String[] args) {
        consumeString(
            s -> System.out.println(s.toUpperCase()),
            s -> System.out.println(s.toLowerCase()+",老婆"),
                "hello");
    }
}

2.3 Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使java.util.function.Predicate<T>接口。
public class PredicateDemo {
    private static void method(Predicate<String> predicate) {
        boolean veryLong = predicate.test("HelloWorld");
        System.out.println("字符串很长吗:" + veryLong);
    }

    public static void main(String[] args) {
        method(s -> s.length() > 5);
    }
}

2.4 Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
public class FunctionDemo {
    private static void method(Function<String, Integer> function) {
        int num = function.apply("10");
        System.out.println(num);
    }

    public static void main(String[] args) {
        method(s -> Integer.parseInt(s));
    }
}

3.泛型的使用

- <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类;
<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object;

3.1代码示例

//比如,我们现在定义:List<? extends T>首先你很容易误解它为继承于T的所有类的集合,你可能认为,你定义的这个List可以用来put任何T的子类,那么我们看下面的代码

import java.util.LinkedList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        List<? extends Father> list = new LinkedList<>();
        list.add(new Son());
    }
}
class Human{
}
class Father extends Human{
}
class Son extends Father{
}
class LeiFeng extends Father {
}


3.2 代码分析

//list.add(new Son());这行会报错:The method put(Son) is undefined for the type List<capture#1-of ? extends Father>

List 表示 “具有任何从Father继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List 赋值。

//也许可以尝试这么做
List<? extends Father> list = new LinkedList<Son>();
list.add(new Son());

即使你指明了为Son类型,也不能用add方法添加一个Son对象。

list中为什么不能加入Father类和Father类的子类呢,我们来分析下

//List<? extends Father>表示上限是Father,下面这样的赋值都是合法的
List<? extends Father> list1 = new ArrayList<Father>();
List<? extends Father> list2 = new ArrayList<Son>();
List<? extends Father> list3 = new ArrayList<LeiFeng>();

如果List支持add方法的话:

  • list1可以add Father和所有Father的子类;
  • list2可以add Son和所有Son的子类;
  • list3可以add LeiFeng和所有LeiFeng的子类。
//下面代码是编译不通过的:
 list1.add(new Father());//error
 list1.add(new Son());//error 

原因是编译器只知道容器内是Father或者它的派生类,但具体是什么类型不知道。可能是Father?可能是Son?也可能是LeiFeng,XiaoMing?编译器在看到后面用Father赋值以后,集合里并没有限定参数类型是“Father“。而是标上一个占位符:CAP#1,来表示捕获一个Father或Father的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Son或者LeiFeng或者Father编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。

所以通配符``和类型参数的区别就在于,对编译器来说所有的T都代表同一种类型。比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer。

public <T> List<T> fill(T... t);

//但通配符<?>没有这种约束,List<?>单纯的就表示:集合里放了一个东西,是什么我不知道
//所以这里的错误就在这里,List<? extends Father>里什么都放不进去。
//List<? extends Father> list不能进行add,但是,这种形式还是很有用的,虽然不能使用add方法,但是可以在初始化的时候一个Season指定不同的类型。比如:


//List<? extends Father> list1 = getFatherList();//getFatherList方法会返回一个Father的子类的list

另外,由于我们已经保证了List中保存的是Father类或者他的某一个子类,所以,可以用get方法直接获得值

List<? extends Father> list1 = new ArrayList<>();
Father father = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Object object = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Human human = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Son son = (Son)list1.get(0);

下界

//super只能添加Father和Father的子类,不能添加Father的父类,读取出来的东西只能存放在Object类里
List<? super Father> list = new ArrayList<>();
list.add(new Father());
list.add(new Human());//compile error 
list.add(new Son());
Father person1 = list.get(0);//compile error 
Son son = list.get(0);//compile error 
Object object1 = list.get(0);

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Father的基类,那往里存粒度比Father小的都可以。出于对类型安全的考虑,我们可以加入Father对象或者其任何子类(如Son)对象,但由于编译器并不知道List的内容究竟是Father的哪个超类,因此不允许加入特定的任何超类(如Human)。而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。但这样的话,元素的类型信息就全部丢失了

3.3总结

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super
  • extends 可用于返回类型限定,不能用于参数类型限定(换句话说:? extends xxx 只能用于方法返回类型限定,jdk能够确定此类的最小继承边界为xxx,只要是这个类的父类都能接收,但是传入参数无法确定具体类型,只能接受null的传入)。
  • super 可用于参数类型限定,不能用于返回类型限定(换句话说:? supper xxx 只能用于方法传参,因为jdk能够确定传入为xxx的子类,返回只能用Object类接收)。
  • ? 既不能用于方法参数传入,也不能用于方法返回
<?  SomeClass><T extends SomeClass>的区别

//看apache parquet源码时,发现代码各种泛型嵌套,有必要系统整理一下关于泛型的各种知识,在此做一总结。
//首先是名词对应表,不需要记住右边的名字,但需要知道左边的各种用法

List<String>- 参数化的类型 
List<E>- 泛型 
List<?>- 无限制通配符类型 
<E extends SomeClass>- 有限制类型参数 
List <? extends SomeClass>- 有限制通配符类型 
<T extends Comparable<T>> —– 递归类型限制 
static <E> List<E> asList(E[] a)- 泛型方法
    
//下面自己的实验包括代码,标号1是解决题目里描述的问题,其余的标号也是自己遇到的一些关键的问题。


3.4疑问解答

<E extends ClassA> 与 <? extends ClassA>有什么区别?

 答:当我第一次接触这两名词时,感觉他们的功能是一样的,T可以代表任意的子类,?也可以代表任意的子类。 
首先我们明确一下两边的名字,限制类型 & 通配符类型,<E extends ClassA>表示后续都只能使用E进行某些判断或操作,而<? extends ClassA>?表示后续使用时可以是任意的。 
举个<E extends ClassA>最常见的例子,用于比较操作,比如返回“最大值”,“最大值”的定义为:整型、浮点型返回最大值,字符串返回字典序最大者,由于想调用compareTo函数,我们让所有参数都继承Compareble,即T extends Comparable<T>,整个测试代码如下
package test;

/**
 *  定义了 <T extends someClass>, 
 *  里面的代码便只能用somClass的子类T进行比较或其他操作。
 */
public class MaximumTest {

   // determines the largest of three Comparable objects
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {                      
      T max = x; // assume x is initially the largest       
      if ( y.compareTo( max ) > 0 ) {
         max = y; // y is the largest so far
      }
      if ( z.compareTo( max ) > 0 ) {
         max = z; // z is the largest now                 
      }
      return max; // returns the largest object   
   }

   public static void main(String args[])  {
      System.out.printf( "Max of %d, %d and %d is %d\n\n", 
                   3, 4, 5, maximum( 3, 4, 5 ) );

       System.out.printf( "Maxm of %.1f,%.1f and %.1f is %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );

       System.out.printf( "Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}

//注释里已经写清楚了,我们只能用T类型来进行一些操作,我们不能把T替换成?,因为?并不是一个类名,它只是一个通配符,然后举个<? extends ClassA>的例子。


比如我们有一个Stack类,类里提供一个pullAll方法,我们想把一系列元素全部放到堆栈中,如下方法

   // Stack定义
   public class Stack<E> {
       public Stack();
       public E pop();
       public boolean isEmpty();
   }

   // ...
   public <E> void pushAll(Iterable<E> src) {
       for(E e : src) {
           push(e);
       }
   }

这个方法编译时没问题,Iterable src的元素类型与堆栈的类型完全匹配就没有问题。但是假如有一个Stack调用了push(intVal),这里的intVal是Integer类型,这是可以的,因为Integer是Number的一个子类型,但下面的代码会报编译错误

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = "...";
numberStack.pushAll(integers);

因为在Java中,参数化类型是不可变的。所以现在我们的通配符类型就派上用场了,代码如下文章来源地址https://www.toymoban.com/news/detail-417225.html

public void pushAll(Iterable<? extends E> scr) {
    for( E e : src) {
        push(e);
    }
}  
List<Object> o = new ArrayList<Long>(); 报错
//不同于数组Object[] o,Long[] o,因为List<Type1>与List<Type2>不互为子类型or超类型

到了这里,关于java基础~函数式接口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaSE基础50题:25. 查找数组中指定元素(顺序查找)

    给定一个数组,再给定一个元素,找出该元素在数组中的位置。 【概述】 一个一个找,比较慢。 想要快一点的方法,可以使用二分查找,在后续《JavaSE基础50题》专栏中27题中详细讲解。 【代码】 【输出结果】

    2024年02月04日
    浏览(37)
  • Java函数式接口

    3.1 函数式接口概述 函数式接口:有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口只有确保接口中有且仅有一个抽象方法, Java中的Lambda才能顺利地进行推导 如何检测一个接口是不是函数式接口呢? @Funct

    2024年02月08日
    浏览(23)
  • 【Java系列】函数式接口编程

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月05日
    浏览(31)
  • Java基础篇--XML简介

    目录 什么是 XML XML 用途 XML 语法 XML文档声明 根元素 元素 属性 注释 转义字符 CDATA区 处理指令 XML的解析 开发中比较常见的解析方式有三种 DOM解析方式: SAX解析方式: PULL解析方式: 常见的解析开发包 DOM解析原理及结构模型 dom4j技术栗子 XML的约束 约束语法: 内部关联:

    2024年02月11日
    浏览(26)
  • 【Java】反射简介,利用反射打印一个类当中的构造函数,方法和属性。

       📝个人主页:哈__ 期待您的关注  我想要通过反射来打印如下效果的类信息。 Student类如下代码所示。  你是否有思路?如果你不了解反射的话,我来给大家简单的介绍一下反射的使用方法。 1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操

    2024年04月10日
    浏览(35)
  • JAVA中的函数接口,你都用过吗

    公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。 在这篇文章中,我们将通过示例来学习 Java 函数式接口。 只包含一个抽象方法的接口称为函数式接口。 它可以有任意数量的默认静态方法,但只能包含一个抽象方法。它还可以声明对象类

    2024年02月05日
    浏览(38)
  • 【Java基础】压测工具JMeter使用简介

    Apache JMeter是一个基于Java开发的开源性能测试工具,由Apache软件基金会维护 JMeter最初设计用于Web应用测试,但它的功能已经扩展到其他测试领域。JMeter可以用于测试静态和动态资源,如静态文件、Java小服务程序、CGI脚本、Java对象、数据库和FTP服务器等。它能够对服务器、网

    2024年04月27日
    浏览(28)
  • Linux驱动开发基础_在设备树中指定中断以及在代码中获得中断

    目录 1 设备树里中断节点的语法 1.1 设备树里的中断控制器 1.2 设备树里使用中断 2  设备树里中断节点的示例 3 在代码中获得中断 3.1 对于 platform_device  3.2  对于 I2C 设备、SPI 设备 3.3  调用 of_irq_get 获得中断号 3.4 对于 GPIO  参考文档:内核 Documentationdevicetreebindingsin

    2024年02月16日
    浏览(30)
  • 常用Java代码-Java中的Lambda表达式和函数式接口

    Java中的Lambda表达式和函数式接口是Java 8中引入的一种新特性,允许编写简洁、可读性强的代码。Lambda表达式允许将简单的代码块作为参数传递给函数,而函数式接口则是一种只有一个抽象方法的接口,可以用于定义Lambda表达式。 下面是一个Lambda表达式的示例: 在这个例子中

    2024年01月18日
    浏览(40)
  • java通用实现List<自定义对象>中指定字段和指定排序方式

    Person类: 工具类: 结果:

    2024年02月04日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包