JAVA 学习 面试(九)Lambda表达式与泛型

Lambda表达式
// 使用 Lambda 表达式计算两个数的和
MathOperation addition = (a, b) -> a + b;
// 调用 Lambda 表达式
int result = addition.operation(5, 3);
// MathOperation 是一个函数式接口,它包含一个抽象方法 operation,Lambda 表达式 (a, b) -> a + b 实现了这个抽象方法,表示对两个参数进行相加操作。
// 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
Lambda表达式与匿名内部类:
  • 内部类编译之后将会产生两个 class 文件(编译器自动命名Test$1.class),lambda编译后只会产生一个文件 (LambdaTest.class),即 Lambda 表达式不会产生新的类
  • Lambda 表达式中使用 this 关键字时,其指向的是外部类的引用(因为其不会创建匿名内部类)
函数式接口

函数式接口是为使现有的函数友好地支持Lambda表达式而提出的概念

@FunctionalInterface // Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
interface GreetingService {void sayMessage(String message);
}
GreetingService greetService1 = message -> System.out.println("Hello " + message);
  • 函数式接口只定义了唯一的抽象方法的接口(除了隐含的Object对象的公共方法),若增加新的方法,就非函数式接口,编译会失败

  • 函数式接口里还允许定义 java.lang.Object 里的 public 方法,因为任何一个函数式接口的实现,默认都继承了 Object 类,包含了来自 java.lang.Object 里对这些抽象方法的实现

  • 可以在接口中添加默认方法与静态方法,无论几个

@FunctionalInterface
public interface XttblogService{void sayMessage(String message);default void doSomeMoreWork1(){//增加默认方法(default),static和default不能同时使用}static void printHello(){  //增加静态方法(static)System.out.println("Hello");}@Override //Object类中的public方法String toString();
}
// 典型的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.util.Comparator
方法引用

可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式结合使用,可以进一步简化代码。

new Random().ints(10).map(i->Math.abs(i)).forEach(i -> System.out.println(i));
//其中 i -> Math.abs(i)代理了如下代码:
new IntUnaryOperator() {@Overridepublic int applyAsInt(int operand) {return Math.abs(operand);}
}
// 通过方法引用进一步简化:
new Random().ints(10).map(Math::abs).forEach(System.out::println);

四种方法引用:

种类语法例子
静态方法引用ContainingClass::staticMethodNameMath::abs
特定对象的实例方法引用containingObject::instanceMethodNamethis::equals
实例方法的任意一个特定类型的对象引用ContainingClass::staticMethodNameString::concat
构造器引用ClassName::newHashSet::new
@FunctionalInterface // 上表中的3
interface TestInterface<T> {String handleString(T a, String b);
}
class TestClass {String oneString;public String concatString(String a) {return this.oneString + a;}public String startHandleString(TestInterface<TestClass> testInterface, String str) {String result = testInterface.handleString(this, str);return result;}
}public class Test {public static void main(String[] args) {TestClass testClass = new TestClass();testClass.oneString = "abc";String result = testClass.startHandleString(TestClass::concatString, "123");System.out.println(result);//相当于以下效果TestClass testClass2 = new TestClass();testClass2.oneString = "abc";TestInterface theOne2 = (a, b) -> testClass2.concatString(b);String result2 = theOne2.handleString(theOne2, "123");System.out.println(result2);}
}
泛型ref
E - Element (在集合中使用,因为集合中存放的是元素)T - Type(Java 类)K - Key(键)V - Value(值)N - Number(数值类型)

**泛型类:**通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

T可以随便写为任意标识,常见的如T、E、K、V等形式的参数

//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{//在类中声明的泛型整个类里面都可以用,除了静态部分,因为泛型是实例化时声明的。//静态区域的代码在编译时就已经确定,只与类相关class A <E>{T t;}//类里面的方法或类中再次声明同名泛型是允许的,并且该泛型会覆盖掉父类的同名泛型Tclass B <T>{T t;}//静态内部类也可以使用泛型,实例化时赋予泛型实际类型static class C <T> {T t;}public static void main(String[] args) {//报错,不能使用T泛型,因为泛型T属于实例不属于类T t = null;}public static <T> void show(T t){}//key这个成员变量的类型为T,T的类型由外部指定private T key;public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定this.key = key;}public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定return key;}
}

泛型类,是在实例化类的时候指明泛型的具体类型;

public class Generic<T> {private T key;public Generic(T key) {this.key = key;}public T getKey(){return key;}
}
// 当创建一个 Generic< T > 类对象时,会向尖括号 <> 中传入具体的数据类型。
@Test
public void test() {Generic<String> generic = new Generic<>();// 传入 String 类型// <> 中什么都不传入,等价于 Generic<Object> generic = new Generic<>();Generic generic = new Generic();
}
// 传入 String 类型时,原泛型类可以想象它会自动扩展,其类型参数会被替换:
public class Generic { private String key;public Generic(String key) { this.key = key;}public String getKey() { return key;}
}

泛型方法,是在调用方法的时候指明泛型的具体类型 ,当在一个方法签名中的返回值前面声明了一个 < T > 时,该方法就被声明为一个泛型方法。< T >表明该方法声明了一个类型参数 T,并且这个类型参数 T 只能在该方法中使用。当然,泛型方法中也可以使用泛型类中定义的泛型参数。

public class Test<U> {// 该方法只是使用了泛型类定义的类型参数,不是泛型方法public void testMethod(U u){System.out.println(u);}// <T> 真正声明了下面的方法是一个泛型方法, < T >表明该方法声明了一个类型参数 T,并且这个类型参数 T 只能在该方法中使用。为了避免混淆,如果在一个泛型类中存在泛型方法,那么两者的类型参数最好不要同名。public <T> T testMethod1(T t){return t;}
}
// 泛型方法中可以同时声明多个类型参数。
public class TestMethod<U> {public <T, S> T testMethod(T t, S s) {return null;}
}public class Test2<T> {// 泛型类定义的类型参数 T 不能在静态方法中使用// 但可以将静态方法声明为泛型方法,方法中便可以使用其声明的类型参数了public static <E> E show(E one) {return null;}
}

当调用泛型方法时,根据外部传入的实际对象的数据类型,编译器就可以判断出类型参数 T所代表的具体数据类型。

public class Demo {public static void main(String args[]) {GenericMethod d = new GenericMethod(); // 创建 GenericMethod 对象  String str = d.fun("汤姆"); // 给GenericMethod中的泛型方法传递字符串  int i = d.fun(30);  // 给GenericMethod中的泛型方法传递数字,自动装箱  System.out.println(str); // 输出 汤姆System.out.println(i);  // 输出 30GenericMethod.show("Lin");// 输出: 静态泛型方法 Lin}
}class GenericMethod {// 普通的泛型方法public <T> T fun(T t) { // 可以接收任意类型的数据return t;}// 静态的泛型方法public static <E> void show(E one){System.out.println("静态泛型方法 " + one);}
}
  • 当泛型方法的形参列表中有多个类型参数时,在不指定类型参数的情况下,方法中声明的的类型参数为泛型方法中的几种类型参数的共同父类的最小级,直到 Object。
  • 在指定了类型参数的时候,传入泛型方法中的实参的数据类型必须为指定数据类型或者其子类。
public class Test {// 这是一个简单的泛型方法public static <T> T add(T x, T y) {return y;}public static void main(String[] args) {// 一、不显式地指定类型参数//(1)传入的两个实参都是 Integer,所以泛型方法中的<T> == <Integer> int i = Test.add(1, 2);//(2)传入的两个实参一个是 Integer,另一个是 Float,// 所以<T>取共同父类的最小级,<T> == <Number>Number f = Test.add(1, 1.2);// 传入的两个实参一个是 Integer,另一个是 String,// 所以<T>取共同父类的最小级,<T> == <Object>Object o = Test.add(1, "asd");// 二、显式地指定类型参数//(1)指定了<T> = <Integer>,所以传入的实参只能为 Integer 对象int a = Test.<Integer>add(1, 2);//(2)指定了<T> = <Integer>,所以不能传入 Float 对象int b = Test.<Integer>add(1, 2.2);// 编译错误//(3)指定<T> = <Number>,所以可以传入 Number 对象// Integer 和 Float 都是 Number 的子类,因此可以传入两者的对象Number c = Test.<Number>add(1, 2.2);}
}
类型擦除

泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即编译器会在编译期间擦除代码中的所有泛型语法并相应的做出一些类型转换动作。

换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。

public class GenericType {public static void main(String[] args) {ArrayList<String> arrayString = new ArrayList<String>();ArrayList<Integer> arrayInteger = new ArrayList<Integer>();System.out.println(arrayString.getClass() == arrayInteger.getClass());// true}
}
// 在编译期间,所有的泛型信息都会被擦除, ArrayList< Integer > 和 ArrayList< String >类型,在编译后都会变成ArrayList< Object>类型。
public class Caculate<T> {private T num;
}//变为:
public class Caculate {public Caculate() {}// 默认构造器,不用管private Object num;// T 被替换为 Object 类型
}public class Caculate<T extends Number> {private T num;
}// 变为:
public class Caculate {public Caculate() {}// 默认构造器,不用管private Number num;
}
  1. 泛型信息(包括泛型类、接口、方法)只在代码编译阶段存在,在代码成功编译后,其内的所有泛型信息都会被擦除,并且类型参数 T 会被统一替换为其原始类型(默认是 Object 类,若有 extends 或者 super 则另外分析);
  2. 在泛型信息被擦除后,若还需要使用到对象相关的泛型信息,编译器底层会自动进行类型转换(从原始类型转换为未擦除前的数据类型)。
通配符

通配符?是一种特殊的泛型类型,用于限定泛型类型参数的范围,在java中,数组是可以协变的,比如dog extends Animal,那么Animal[] 与dog[]是兼容的。而集合是不能协变的,也就是说List不是List的父类,这时候就可以用到通配符了。

限定通配符包括两种:
\1. 表示类型的上界,格式为:<? extends T>,即类型必须为T类型或者T子类
\2. 表示类型的下界,格式为:<? super T>,即类型必须为T类型或者T的父类

public class Test { //上界public static void printIntValue(List<? extends Number> list) {l.add(1); // 编译报错,不能使用add方法,因为我们在写这个方法时不能确定传入的什么类型的数据for (Number number : list) {System.out.print(number.intValue()+" "); }System.out.println();}public static void main(String[] args) {List<Integer> integerList=new ArrayList<Integer>();integerList.add(2);integerList.add(2);printIntValue(integerList);List<Float> floatList=new ArrayList<Float>();floatList.add((float) 3.3);floatList.add((float) 0.3);printIntValue(floatList);}
}
// 下界
public static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 10; i++) {list.add(i); // 能够调用add方法, 因为这里元素就是Integer类型的, 而传入的list不管是什么, 都一定是Integer或其父类泛型的List}
}
public static void main(String[] args) {List<Object> list1 = new ArrayList<>();addNumbers(list1);System.out.println(list1);List<Number> list2 = new ArrayList<>();addNumbers(list2);System.out.println(list2);List<Double> list3 = new ArrayList<>();// addNumbers(list3); // 编译报错
}
// 所传入的数据类型可能是Integer到Object之间的任何类型, 这是无法预料的, 也就无法接收. 唯一能确定的就是Object, 因为所有类型都是其子类型.
public static void getTest2(List<? super Integer> list) {// Integer i = list.get(0); //编译报错Object o = list.get(1);
}

下界通配符的另一个常见用法:TreeSet t = TreeSet(Comparator<? super E> comparator) ,在Comparator中可以利用类E的任意一个父类的属性进行排序,只有元素是?的子类型的TreeSet才可以应用这个comparator,如下:

public class Person {private String name;private int age;/** 构造函数与getter, setter省略*/
}public class Student extends Person {public Student() {}public Student(String name, int age) {super(name, age);}
}class comparatorTest implements Comparator<Person>{@Overridepublic int compare(Student s1, Student s2) {int num = s1.getAge() - s2.getAge();return num == 0 ? s1.getName().compareTo(s2.getName()) :  num;}
}public class GenericTest {public static void main(String[] args) {TreeSet<Person> ts1 = new TreeSet<>(new comparatorTest());ts1.add(new Person("Tom", 20));ts1.add(new Person("Jack", 25));ts1.add(new Person("John", 22));System.out.println(ts1);TreeSet<Student> ts2 = new TreeSet<>(new comparatorTest());ts2.add(new Student("Susan", 23));ts2.add(new Student("Rose", 27));ts2.add(new Student("Jane", 19));System.out.println(ts2);}
}

非限定通配符:无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>,无边界的通配符的主要作用就是让泛型能够接受未知类型的数据.

/* 这种使用List<?>的方式就是父类引用指向子类对象. 注意, 这里不能printList(List<Object> list)的形式, , 虽然Object类是所有类的父类, 但是List<Object>跟其他泛型的List如List<String>, List<Integer>不存在继承关系,  */
public static void printList(List<?> list) {for (Object o : list) {System.out.println(o);}
}public static void main(String[] args) {List<String> l1 = new ArrayList<>();l1.add("aa");l1.add("bb");l1.add("cc");printList(l1);List<Integer> l2 = new ArrayList<>();l2.add(11);l2.add(22);l2.add(33);printList(l2);
}
// 我们不能对List<?>使用add方法, 仅有一个例外, 就是add(null). 为什么呢? 因为我们不确定该List的类型, 不知道add什么类型的数据才对, 只有null是所有引用数据类型都具有的元素
public static void addTest(List<?> list) {Object o = new Object();list.add(o); // 编译报错list.add(1); // 编译报错list.add("ABC"); // 编译报错list.add(null);
}
// Object是所有数据类型的父类, 所以只有接受他可以
public static void getTest(List<?> list) {String s = list.get(0); // 编译报错Integer i = list.get(1); // 编译报错Object o = list.get(2);
}

因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。

常见泛型使用错误
不可以给Array加泛型
Array<Integer>,//Type Array does not have type parameters
//而使用通配符创建泛型数组是可以的,如下面这个例子:
List<?>[] ls = new ArrayList<?>[10];  Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯Vector<Object> v = new Vector<String>(); //也错误!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/645929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

this.$copyText;vue-clipboard2作用;vue-clipboard2剪切板

1.安装 npm install --save vue-clipboard2 2.在main.js中引用 import Vue from vue import VueClipBoard from vue-clipboard2 Vue.use(VueClipBoard) 3.代码中使用 <button click"Copy">复制</button> Copy() { this.$copyText(this.value).then…

蓝桥杯备赛 week 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

css Media媒体查询常用属性

使用@media规则声明媒体查询,主要用于控制在不同的设备上显示不同的效果 媒体类型: screen 适用于电脑屏幕、平板电脑、智能手机等 print 适用于打印预览 特性 width 可视区域的宽度 orientation 视窗的旋转方向(横屏landscape,默认竖屏模式)。 运算符: and 并且 , 或…

Linux/Academy

Enumeration nmap 首先扫描目标端口对外开放情况 nmap -p- 10.10.10.215 -T4 发现对外开放了22,80,33060三个端口&#xff0c;端口详细信息如下 结果显示80端口运行着http&#xff0c;且给出了域名academy.htb&#xff0c;现将ip与域名写到/et/hosts中&#xff0c;然后从ht…

Mysql 文件导入与导出

i/o 一、导出(mysqldump)<一>、导出sql文件<二>、导出csv文件 二、导入(load)三、常见报错The Mysql server is running with the --secure-file-priv option so it cannot execute this statement 一、导出(mysqldump) <一>、导出sql文件 1、整库 mysqld…

【12.PWM输出】蓝桥杯嵌入式一周拿奖速成系列

系列文章目录 蓝桥杯嵌入式系列文章目录(更多此系列文章可见) PWM输出 系列文章目录一、STM32CUBEMX配置二、项目代码1.main.c --> PWMOutputProcess 总结 一、STM32CUBEMX配置 STM32CUBEMX PA6 ->TIM16_CH1; PA7-> TIM17_CH1 预分频设置为79,自动重装载设置999PWM输…

PyQtGraph 之PlotCurveItem 详解

PyQtGraph 之PlotCurveItem 详解 PlotCurveItem 是 PyQtGraph 中用于显示曲线的图形项。以下是 PlotCurveItem 的主要参数和属性&#xff1a; 创建 PlotCurveItem 对象 import pyqtgraph as pg# 创建一个 PlotCurveItem curve pg.PlotCurveItem()常用的参数和属性 setData(…

资源管理核心考点梳理

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 PMP - 资源管理核心考点梳理 资源管理包括人力资源和实物资源管理。学习的重点是人力资源的管理&#xff0c;这一章是考试的重点章节&#xff0c;在新考纲中&#xff0c;“人”这一模块在题目种的比例是42%。 01 …

在uvm中,以svi结尾和sv结尾文件的区别

在UVM&#xff08;Universal Verification Methodology&#xff09;中&#xff0c;.sv和.svi文件扩展名通常是SystemVerilog文件的标准扩展名。它们都用来标识SystemVerilog源代码文件。然而&#xff0c;不同项目或团队可能会采用不同的命名约定来区分不同类型的SystemVerilog文…

14.块参照的旋转(BlockReference)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET FrameWork4.5、ObjectArx 2016 64bit、Entity Framework 6. 在排水管网数据的编图时&#xff0c;时常会遇见针对雨水箅等进行旋转。由于数据存储在数据库内&#xff0c;通过CAD自带的旋转功能只能变更图面而无法…

YOLOv8改进 | Conv篇 | 利用轻量化PartialConv提出一种全新的结构CSPPC (参数量下降约100W)

一、本文介绍 本文给大家带来的改进机制是由我独家研制的,我结合了DualConv的思想并根据PartialConv提出了一种全新的结构CSPPC用来替换网络中的C2f,将其替换我们网络中的C2f参数量后直接下降约百万,计算量GFLOPs降低至6.0GFLOPs同时,其中的PartialConv作为一种具有高速推…

SVG 矩形 – SVG Rectangle (3)

简介 rect 元素用于创建 SVG 矩形和矩形图形的变体。有六个属性决定矩形在屏幕上的形状和位置 x, y – 矩形左上角的 x, y 坐标width、height – 矩形的宽度和高度rx、ry – 矩形角的 x 和 y 半径 如果没有设置 x 和 y 属性&#xff0c;则矩形的左上角放置在点 (0,0) 处。 如…

Python 中的多进程(01/2):简介

一、说明 本文简要而简明地介绍了 Python 编程语言中的多处理&#xff08;多进程&#xff09;。解释多处理的基本信息&#xff0c;如什么是多处理&#xff1f;为什么用多处理&#xff1f;在python中怎么办等。 二、什么是多处理&#xff1f; 多处理是指系统同时支持多个处理器的…

C语言第八弹---一维数组

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 一维数组 1、数组的概念 2、⼀维数组的创建和初始化 2.1、数组创建 2.2、数组的初始化 2.3、数组的类型 3、⼀维数组的使用 3.1、数组下标 3.2、数组元素…

Vscode配置python代码开发

文章目录 1. 配置python运行环境2. 常用插件说明3. Vscode配置文件说明3.1 setting.json配置说明3.2 launch.json配置说明 4. 远程开发5. 其他配置 1. 配置python运行环境 安装python插件&#xff1a;点击VSCode左侧边栏中的扩展图标&#xff08;或按 CtrlShiftX&#xff09;&a…

从方法论到最佳实践,深度解析企业云原生 DevSecOps 体系构建

作者&#xff1a;匡大虎 引言 安全一直是企业上云关注的核心问题。随着云原生对云计算基础设施和企业应用架构的重定义&#xff0c;传统的企业安全防护架构已经不能够满足新时期下的安全防护要求。为此企业安全人员需要针对云原生时代的安全挑战重新进行系统性的威胁分析并构…

ubuntu20.04 格式化 硬盘 扩展硬盘

如何在 Ubuntu 22.04 LTS 上安装分区编辑器 GParted&#xff1f;_gparted安装-CSDN博客 sudo apt install gparted 步骤5&#xff1a;启动GParted 安装完成后&#xff0c;您可以在应用程序菜单中找到GParted。点击它以启动分区编辑器。 通过以上步骤&#xff0c;您可以在Ubun…

深度视觉目标跟踪进展综述-论文笔记

中科大学报上的一篇综述&#xff0c;总结得很详细&#xff0c;整理了相关笔记。 1 引言 目标跟踪旨在基于初始帧中指定的感兴趣目标( 一般用矩形框表示) &#xff0c;在后续帧中对该目标进行持续的定位。 基于深度学习的跟踪算法&#xff0c;采用的框架包括相关滤波器、分类…

CSIT883系统分析与项目管理——Lecture9重点概念

文章目录 一、前言二、重点概念三、题目分析一、前言 本次课的主要内容有系统设计基础与项目沟通管理。系统设计基础当中有许多重点概念,比如设计完整性控制与如何进行系统设计,访问控制等等。项目沟通管理当中主要介绍如何在项目团队当中进行沟通的方法,面对不同的人,不同…

Rust 通用代码生成器莲花发布红莲尝鲜版二十视频,支持 Nodejs 21,18 和 14

Rust 通用代码生成器莲花发布红莲尝鲜版二十视频&#xff0c;支持 Nodejs 21,18 和 14 Rust 通用代码生成器莲花发布红莲尝鲜版二十视频。此版本开始支持 Nodejs21,18 加上原来支持的 Nodejs 14。现在莲花支持三种 Nodejs 环境。适应性大大增强&#xff0c;也给您的使用带来了…