从零开始学java--泛型

泛型

目录

泛型

引入

泛型类

泛型与多态

泛型方法

泛型的界限

类型擦除

函数式接口

Supplier供给型函数式接口:

Consumer消费型函数式接口:

Function函数型函数式接口:

Predicate断言式函数式接口:

判空包装


引入

学生成绩可能是数字类型,也可能是字符串类型,如何存放可能出现的两种类型呢:

public class Score {String name;String id;Object value;  //因为Object是所有类型的父类,因此既可以存放Integer也能存放Stringpublic Score(String name,String id,Object value){this.name=name;this.id=id;this.value=value;}
}

以上方法虽然解决了多种类型存储的问题,但是Object类型在编译阶段并不具有良好的类型判断能力,很容易出现:

    public static void main(String[] args) {Score score=new Score("数学","aa","优秀"); //是String类型的Integer number=(Integer) score.getValue();//获取成绩需要进行强制类型转换,虽然并不是一开始的类型,但是编译不会报错}

由于是Object类型,所以并不能直接判断存储的到底是String还是Integer,取值只能进行强制类型转换,显然无法在编译期确定类型是否安全,项目中代码量非常大,进行类型比较又会导致额外的开销和增加代码量,如果不比较又容易出现类型转换异常,代码的健壮性有所欠缺。

为了解决以上问题,JDK5新增了泛型,它能够在编译阶段检查类型安全,大大提升开发效率。


泛型类

定义泛型类:


public class Score<T> { //泛型类需要使用<>,在里面添加1-N个类型变量String name;String id;T value;  //T会根据使用时提供的类型自动变成对应类型public Score(String name,String id,T value){  //这里的T可以是任何类型,但是一旦确定就不能修改了this.name=name;this.id=id;this.value=value;}
}
public static void main(String[] args) {Score<String> score=new Score<>("数学","aa","优秀");//使用时跟上<>并在其中填写明确要使用的类型}

泛型将数据类型控制在了编译阶段, 在编写代码时就能明确泛型的类型,类型不符合将无法编译通过。

1、因为是具体使用对象时才会明确具体类型,所以说静态方法中不能用。

2、方法中使用待确定类型的变量时,因为不明确类型则会默认这个变量是一个Object类型的变量(即不能使用String等类型中的方法)。可对其进行强制类型转换但没必要。

3、不能通过这个不确定的类型变量直接创建对象和对应的数组。

4、具体类型不同的泛型类变量,不能使用不同的变量进行接收。

5、如果要让某个变量支持引用确定了任意类型的泛型,可以使用?通配符。 

public static void main(String[] args) {Score<String> score=new Score<>("数学","aa","优秀");Score<?>score1=score;}

 如果使用通配符,由于类型不确定,所以说具体类型同样会变成Object。

6、泛型变量可以定义多个,多个类型变量用,隔开。在使用时需要将这三种类型都进行明确指令。

7、泛型只能确定为一个引用类型,不支持基本类型。

要存放基本数据类型的值,我们只能使用对应的包装类。

如果是基本类型的数组,因为数组本身是引用类型,所以是可以的。


泛型与多态

不只是类,包括接口、抽象类都可以支持泛型:

public static void main(String[] args) {Score<String> score=new Score<>("数学","aa","优秀");Score<?>score1=score;}

当子类实现此接口时,我们可以选择在实现类明确泛型类型:

public static void main(String[] args) {A a=new A();Integer i=a.study();}static class A implements Study<Integer>{
//在实现接口或是继承父类时,如果子类是一个普通类,那么可以直接明确对应类型@Overridepublic Integer study() {return 0;}}

也可以继续使用泛型:

public static void main(String[] args) {A<Integer> a=new A<>();Integer i=a.study();}static class A<T> implements Study<T> {
//让子类继续为一个泛型类,那么可以不明确@Overridepublic T study() {return null;}}

 继承:

static class A<T>{}static class B extends A<String>{}

 


泛型方法

泛型变量不仅仅在泛型类中使用,也可以定义泛型方法。

当某个方法(无论是静态方法还是成员方法)需要接受的参数类型不确定时,我们可以使用泛型来表示:

public static void main(String[] args) {String str=test("10");}public static <T>T test(T t){ //在返回值类型前添加<>并填写泛型变量表示这是一个泛型方法return t;}

泛型方法会在使用时自动确定泛型类型,比如我们定义的是类型T作为参数,同样的类型T作为返回值,实际传入的参数是一个字符串类型的值,那么T就会自动变成String类型,因此返回值也是String类型。

泛型方法在很多工具类中也有,比如说Arrays的排序方法:

public static void main(String[] args) {Integer[] arr = {1, 3, 2, 7, 4, 9, 0};//不能比较基本数据类型intArrays.sort(arr, new Comparator<Integer>() {//通过创建泛型接口的匿名内部类,来自定义排序规则,因为匿名内部类就是接口的实现类,所以这里就明确了类型@Overridepublic int compare(Integer o1, Integer o2) {  //这个方法会在执行排序时被调用(别人调用我们的实现)//想要让数据从大到小排列:return o2-o1;//compare方法要求返回一个int来表示两个数的大小关系,大于0表示大于,小于0表示小于//如果o2比o1大,那么应该排在前面,所以说返回正数表示大于}});System.out.println(Arrays.toString(arr));}

可替换为Lambda表达式:

public static void main(String[] args) {Integer[] arr = {1, 3, 2, 7, 4, 9, 0};Arrays.sort(arr, (o1, o2) -> o2-o1);System.out.println(Arrays.toString(arr));}


泛型的界限

若现在没有String类型的成绩了,但是成绩依然可能是整数或小数,我们不希望将泛型指定为除数字类型外的其他类型,就需要使用到泛型的上界定义。

只需要在泛型变量的后面添加extends关键字即可指定上界:


public class Score<T extends Number> { //设定类型参数上界,必须是Number或Number的子类String name;String id;T value;public Score(String name,String id,T value){this.name=name;this.id=id;this.value=value;}public T getValue() {return value;}
}

泛型通配符也支持泛型的界限:

public static void main(String[] args) {Score<? extends Integer>score=new Score<>("xm","11",10);}

下界只适用于通配符,对于类型变量来说是不支持的。

public static void main(String[] args) {Score<? super Object>score=new Score<>("xm","11",10);}

entends定义的只能存放它自己及其子类,super定义的只能存放它自己及其父类。

限定上界后使用这个对象的泛型成员:

public static void main(String[] args) {Score<? extends Number>score=new Score<>("xm","11",10);Number o=score.getValue();  //此时虽然使用的是通配符,但是不再是Object类型,而是对应的上界}

 限定下界的话,因为还有可能是Object,所以说依然和之前一样:

public static void main(String[] args) {Score<? super Number>score=new Score<>("xm","11",10);Object o=score.getValue();}

 


类型擦除

实际上在Java中并不是真的有泛型类型,因为所有的对象都是一个普通的类型,一个泛型类型编译之后,实际上会直接使用默认的类型。

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。

如果我们给类型变量设定了上界,那么会从默认类型变成上界定义的类型。

泛型其实仅仅是在编译阶段进行类型检查,当程序在运行时,并不会真的去检查对应类型,所以哪怕我们不指定类型也可以使用。

擦除机制其实就是为了方便使用后面集合类(否则每次都要强制类型转换)同时为了向下兼容采取的方案,因此泛型的使用会有一些限制:

首先,在进行类型判断时,不允许使用泛型,只能使用原始类型:

    public static void main(String[] args) {Test<String> test =new Test<>();System.out.println(test instanceof Test);}

其次,泛型不支持创建参数化类型数组的:

只不过只是把它当做泛型类型的数组还是可以用的:


函数式接口

@FunctionalInterface 函数式接口都会打上这样的注解

满足Lambda表达式的需求有且仅有一个需要去实现(未实现)的方法。

函数式接口就是JDK1.8专门提供好的用于Lambda表达式的接口,这些接口都可以直接使用Lambda表达式。以下主要介绍四个主要的函数式接口:

Supplier供给型函数式接口:

这个接口是专门用于供给使用的,其中只有一个get方法用于获取需要的对象。

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get(); //实现此方法,实现供给功能
}
    public static void main(String[] args) {Supplier<Student> studentSupplier= Student::new;studentSupplier.get().hello();}public static class Student{public void hello(){System.out.println("我是学生");}}

Consumer消费型函数式接口:

这个接口专门用于消费某个对象。

@FunctionalInterface
public interface Consumer<T> {void accept(T t); //这个方法用于消费,没有返回值default Consumer<T> andThen(Consumer<? super T> after) {  //默认实现,这个方法便于我们连续使用此消费接口Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}
public class Main {
//专门消费Student对象的Consumerprivate static final Consumer<Student> STUDENT_COMPARATOR=student->System.out.println(student+"看不懂");public static void main(String[] args) {Student student=new Student();STUDENT_COMPARATOR.accept(student);}public static class Student{public void hello(){System.out.println("我是学生");}}
}
    public static void main(String[] args) {Student student=new Student();STUDENT_COMPARATOR   //可以使用andThen方法继续调用,将消费之后的操作以同样的方式预定好.andThen(student1 -> System.out.println("后续操作")).accept(student);}//输出
com.test.Main$Student@404b9385看不懂
后续操作

Function函数型函数式接口:

这个接口消费一个对象,然后会向外供给一个对象(前两个的融合体)

@FunctionalInterface
public interface Function<T, R> {R apply(T t);  //这里一共有两个类型参数,其中一个是接受的参数类型,另一个是返回的结果类型default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;}
}

apply方法: 

//这里实现了一个简单的功能,将传入的int参数转换为字符串的形式private static final Function<Integer,String>INTEGER_STRING_FUNCTION=Objects::toString;
//Integer输入,String输出?public static void main(String[] args) {String str=INTEGER_STRING_FUNCTION.apply(10);System.out.println(str);}

使用compose将指定函数式的结果作为当前函数式的实参:(compose是前置工作)

    public static void main(String[] args) {String str=INTEGER_STRING_FUNCTION.compose((String s)->s.length())  //将此函数式的返回值作为当前实现的实参.apply("aaa");  //传入上面函数式需要的参数System.out.println(str);//String ->Integer ->String// aaa   ->  3     -> "3"}//输出3

andThen可以将当前实现的返回值进行进一步的处理,得到其他类型的值:(后续工作)

    public static void main(String[] args) {Boolean str=INTEGER_STRING_FUNCTION.andThen(String::isEmpty)  //在执行完后,返回值作为参数执行andThen内的函数式,最后得到的结果就是最终的结果了.apply(10);System.out.println(str);}
//输出false

还提供了一个将传入参数原样返回的实现:

    public static void main(String[] args) {Function<String,String>function=Function.identity();System.out.println(function.apply("aaaa"));}
//输出aaaa

Predicate断言式函数式接口:

接收一个参数,然后进行自定义并返回一个boolean结果。

@FunctionalInterface
public interface Predicate<T> {boolean test(T t);  //要实现的方法default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}default Predicate<T> negate() {return (t) -> !test(t);}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);}
}
import java.util.function.Predicate;public class Main {private static final Predicate<Student> STUDENT_PREDICATE=student -> student.score>=60;public static void main(String[] args) {Student student=new Student();student.score=80;if (STUDENT_PREDICATE.test(student)){  //test方法的返回值是一个boolean结果System.out.println("及格了");}else {System.out.println("不及格");}}public static class Student{int score=100;public void hello(){System.out.println("我是学生");}}
}


判空包装

判空包装类Optional,这个类可以很有效的处理空指针问题。

    public static void main(String[] args) {test(null);}public static void test(String str){  //传入字符串,如果不是空串就打印长度if(str == null)return; //没有这句若传入null会出现空指针异常错误if(!str.isEmpty()){System.out.println("长度为"+str.length());}}

用Optional类处理上述问题:

    public static void test(String str){Optional.ofNullable(str)  //将传入的对象包装进Optional中.ifPresent(s -> System.out.println("长度为"+s.length()));//如果不为空(ifPresent)则执行这里的Consumer实现}

其他的一些使用:

//不为空输出字符串,为空...public static void test(String str){String s1=Optional.ofNullable(str).orElse("为null的备选情况");System.out.println(s1);}
//将包装的类型直接转换为另一种类型public static void main(String[] args) {test("aaaaa");}public static void test(String str){Integer i=Optional.ofNullable(str).map(s -> s.length()).get();System.out.println(i);}
//输出5

......

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

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

相关文章

5️⃣ Coze+AI应用基础教学(2025年全新版本)

目录 一、了解应用开发 1.1 扣子应用能做什么 1.2 开发流程 1.3 开发环境 二、快速搭建一个AI应用 2.1 AI翻译应用介绍 2.2 设计你的应用功能 2.3 创建 AI 应用项目 2.4 编写业务逻辑(新建工作流) 2.5 搭建用户界面 2.6 效果测试 2.7 发布应用 一、了解应用开发 …

工会成立100周年纪念,开发职工健身AI运动小程序、APP方案推荐

时光荏苒&#xff0c;转眼间2025年五一将至&#xff0c;这一年对于中华全国总工会而言&#xff0c;具有非凡的历史意义——它将迎来成立100周年的辉煌时刻。为了庆祝这一盛事&#xff0c;各级工会组织将精心筹备了一系列丰富多彩、形式多样的纪念活动&#xff0c;旨在展现工会百…

【深度学习】Ubuntu 服务器配置开源项目FIGRET(PyTorch、torch-scatter、torch-sparse、Gurobi 安装)

开源项目网址&#xff1a;https://github.com/FIGRET/figret 该项目在SIGCOMM2024发表&#xff0c;用深度学习方法处理流量工程中的突发问题 1. 创建新的 Conda 环境 使用国内镜像源创建环境​ conda create -n figret python3.8.0 --override-channels -c https://mirrors.…

【SpringCloud】从入门到精通(上)

今天主播我把黑马新版微服务课程MQ高级之前的内容都看完了&#xff0c;虽然在看视频的时候也记了笔记&#xff0c;但是看完之后还是忘得差不多了&#xff0c;所以打算写一篇博客再温习一下内容。 课程坐标:黑马程序员SpringCloud微服务开发与实战 微服务 认识单体架构 单体架…

MySQL中动态生成SQL语句去掉所有字段的空格

在MySQL中动态生成SQL语句去掉所有字段的空格 在数据库管理过程中&#xff0c;我们常常会遇到需要对表中字段进行清洗和整理的情况。其中&#xff0c;去掉字段中的空格是一项常见的操作。当表中的字段数量较少时&#xff0c;我们可以手动编写 UPDATE 语句来处理。但如果表中包…

【Grok 大模型深度解析】第二期:架构探秘与训练哲学

在上一期的内容中,我们对 Grok 大模型从技术溯源的角度,了解了它从 Transformer 架构局限性出发,迈向混合架构创新的历程,同时也梳理了从 Grok - 1 到 Grok - 3 的版本迭代所带来的技术跃迁以及其独特的差异化优势。这一期,我们将深入到 Grok 大模型的架构内部,探究其精妙…

c# 使用NPOI将datatable的数据导出到excel

以下是使用 NPOI 库 将 DataTable 数据导出到 Excel 的详细步骤和代码示例(支持 .xls 和 .xlsx 格式): 步骤 1:安装 NPOI NuGet 包 Install-Package NPOI Install-Package NPOI.OOXML # 若需导出 .xlsx 格式 步骤 2:完整代码实现 using NPOI.SS.UserModel; using NPOI.…

基于SpringBoot的求职招聘网站系统(源码+数据库)

473基于SpringBoot的求职招聘网站系统&#xff0c;本系统共分为2个角色&#xff1a;系统管理员、用户&#xff0c;主要功能如下 【前台功能】 用户角色功能&#xff1a; 1. 注册和登录&#xff1a;注册账户并登录系统&#xff0c;以便访问更多功能。 2. 个人信息管理&#x…

CSS 过渡与变形:让交互更丝滑

在网页设计中&#xff0c;动效能让用户交互更自然、流畅&#xff0c;提升使用体验。本文将通过 CSS 的 transition&#xff08;过渡&#xff09;和 transform&#xff08;变形&#xff09;属性&#xff0c;带你入门基础动效设计&#xff0c;结合案例演示如何实现颜色渐变、元素…

rqlite:一个基于SQLite构建的分布式数据库

今天给大家介绍一个基于 SQLite 构建的轻量级分布式关系型数据库&#xff1a;rqlite。 rqlite 基于 Raft 协议&#xff0c;结合了 SQLite 的简洁性以及高可用分布式系统的稳健性&#xff0c;对开发者友好&#xff0c;操作极其简便&#xff0c;其核心设计理念是以最低的复杂度实…

mujoco graspnet 仿真项目的复现记录

开源项目&#xff1a;https://gitee.com/chaomingsanhua/manipulator_grasp 复现使用的配置&#xff1a;linux系统ubuntu20.04 项目配置记录&#xff1a; git clone 对应的code后&#xff1a; 需要在graspnet-baseline文件夹中继续拉取文件&#xff0c;指令记录&#xff1a;…

【js面试题】new操作做了什么?

这些年也面试了一些外包同事&#xff0c;不知道其他面试官的想法&#xff0c;但就我而言&#xff0c;我更喜欢听到的是口述代码的方式&#xff1a; 比如下述代码 function Animal(age) {this.age age; // 设置新对象的属性 }const cat new Animal("8");最有效的回…

freecad内部python来源 + pip install 装包

cmake来源&#xff1a; 只能find默认地址&#xff0c;我试过用虚拟的python地址提示缺python3config.cmake python解释器位置&#xff1a; python控制台位置&#xff1a; pip install 装包&#xff1a; module_to_install "your pakage" import os import FreeCAD …

树和图论【详细整理,简单易懂!】(C++实现 蓝桥杯速查)

树和图论 树的遍历模版 #include <iostream> #include <cstring> #include <vector> #include <queue> // 添加queue头文件 using namespace std;const int MAXN 100; // 假设一个足够大的数组大小 int ls[MAXN], rs[MAXN]; // 定义左右子树数…

展讯android15源码编译之apk单编

首先找到你要单编的apk生成的路径&#xff1a; sys\out_system\target\product\ussi_arm64\system_ext\app\HelloDemo\HelloDemo.apk接着打开下面这个文件&#xff1a; sys\out_system\ussi_arm64_full-userdebug-gms.system.build.log在里面找关键字"Running command&q…

如何关闭MacOS中鼠标滚轮滚动加速

一、背景 想要关闭滚轮的 “滚动加速”&#xff0c;即希望滚动了多少就对应滚动页面固定行数&#xff0c;现在macOS是加速滚动的&#xff0c;即滚动相同的角度会根据你滚动滚轮的速度不同最终页面滚动的幅度不同。这点很烦&#xff0c;常导致很难定位。 macOS本身的设置是没有…

河北工程大学e2e平台,python

题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 联系&#x1f6f0;&#xff1a;18039589633

【蓝桥杯】贪心算法

1. 区间调度 1.1. 题目 给定个区间,每个区间由开始时间start和结束时间end表示。请选择最多的互不重叠的区间,返回可以选择的区间的最大数量。 输入格式: 第一行包含一个整数n,表示区间的数量 接下来n行,每行包含两个整数,分别表示区间的开始时间和结束时间 输出格式:…

一维差分数组

2.一维差分 - 蓝桥云课 问题描述 给定一个长度为 n 的序列 a。 再给定 m 组操作&#xff0c;每次操作给定 3 个正整数 l, r, d&#xff0c;表示对 a_{l} 到 a_{r} 中的所有数增加 d。 最终输出操作结束后的序列 a。 ​​Update​​: 由于评测机过快&#xff0c;n, m 于 20…

二分答案----

二分答案 - 题目详情 - HydroOJ 问题描述 给定一个由n个数构成的序列a&#xff0c;你可以进行k次操作&#xff0c;每次操作可以选择一个数字&#xff0c;将其1&#xff0c;问k次操作以后&#xff0c;希望序列里面的最小值最大。问这个值是多少。 输入格式 第一行输入两个正…