java中的泛型(通过泛型实现多类型的抽奖机),对象成员变量赋值的优先级

一、什么是泛型
1.背景:

JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。(例如:4.理解里的list,遍历时就会出现强制类型转换,出现错误,不提前定义数据类型,任何数据类型都可以存储,等你获取的时候就会报错)

2.概念:
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

3.好处:
类型安全
消除了强制类型的转换
4.理解
操作数据类型参数化。
在这里插入图片描述
5.类型

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(表示Java 类,包括基本的类和我们自定义的类)
  • K - Key(表示键,比如Map中的key)
  • V - Value(表示值)
  • N - Number(表示数值类型)
  • ? - (表示不确定的java类型)
  • S、U、V - 2nd、3rd、4th types

6.举例说明
如下就是一个泛型类,当我们创建对象的时候,才会对T进行定义类型(Integer String等)

/**
 * @param <T> 泛型标识---类型形参
 *           T 创建对象时指定的具体的数据类型
 *由外部使用类来指定T
 * */
public class Queue<T> implements Iterable<T> {
 private T t;
}
例如:
Queue<String> q=new Queue<String>();

7.使用

1)使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
List<String> list1=new ArrayList<String>();2)Java1.7以后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>(); 
 List<String> list1=new ArrayList<>();

8.注意

  • 不指定类型就是Object类型
  • 不支持基本数据类型(int char float boolean等八大基本数据类型)
  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
//有Generic<T>这个类
		 Generic<String> strGeneric = new Generic<String>,
		 Generic<Integer> intGeneric = new Generic<Integer>
		 intGeneric.getClass() == strGeneric.getClass()//结果是true

抽奖机实现:
抽奖机类

package choujiang;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ProductGetter<T> {
    Random random = new Random();
    private T product;

    List<T> list = new ArrayList<>();

    public void addProduct(T t) {
        list.add(t);
    }

    public T getProduct() {
        return list.get(random.nextInt(list.size()));
    }
}

测试类(物品-----String)

package choujiang;

public class Test {
    public static void main(String[] args) {
        ProductGetter<String> productGetter=new ProductGetter<>();
        String [] pool={"苹果","桃子","李子","杏"};
        for (int i = 0; i < pool.length; i++) {
            productGetter.addProduct(pool[i]);
        }

        System.out.println("你抽中的是:"+productGetter.getProduct());
    }

}

测试类(奖金-----Integer)

package choujiang;

public class Test {
    public static void main(String[] args) {
        ProductGetter<Integer> productGetter=new ProductGetter<>();
        Integer [] pool={100,200,300,400};
        for (int i = 0; i < pool.length; i++) {
            productGetter.addProduct(pool[i]);
        }

        System.out.println("你抽中的是:"+productGetter.getProduct());
    }

}

**拓展:**创建对象赋值的过程
java中调用构造函数,意味着创建对象吗???(调用构造函数只是创建对象的一步)
代码解释

public class Parent {
    private String name;
    private int age;

    public Parent(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Parent() {
    }
}


 public static void main(String[] args) {
        Parent p=new Parent("yangzhenxu",25);
    }

第一步:分配对象空间,并将对象中成员初始化为0或者空,java不允许用户操纵一个不定值的对象。(申请Parent对象的空间,并对成员初始化)。
第二步:执行属性值的显式初始化。(就是对name age显示初始化)
第三步:执行构造器Parent(“yangzhenxu”,25);
第四步:将变量关联到堆中的对象上。
解释:显式初始化
就是由编写的人员通过语句进行初始化
比如说你重新写了构造方法就是显式的
如果你没重写构造方法那么系统有个默认的构造方法就是隐式的。

package constructor;

public class Parent {
    private String name;
    //显式赋值
    private int age=1;

    public Parent(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Parent() {
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
//构造器赋值
 Parent p=new Parent("1",1);
//对象.方法
p.setAge(1);

(默认初始化 --> 显式初始化 --> 构造器中赋值 --> 通过“对象.方法”或者“对象.属性”的方式,赋值)
在这里插入图片描述
也就是越后边的操作,会覆盖前边的操作。

9.泛型类派生子类

  • 子类也是泛型类,子类和父类的泛型类型要一致
//父类
public class Parent<E> {
    private E value;
    public E getValue() {
        return value;
    }
    public void setValue(E value) {
        this.value = value;
    }
}

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * @param <T>
 */
public class ChildFirst<T> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

  • 子类不是泛型类,父类要明确泛型的数据类型
/**
 * 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
 */
public class ChildSecond extends Parent<Integer> {
    @Override
    public Integer getValue() {
        return super.getValue();
    }
    @Override
    public void setValue(Integer value) {
        super.setValue(value);
    }
}

10.泛型接口
泛型接口的定义语法:

interface 接口名称 <泛型标识,泛型标识,…> {
	泛型标识 方法名(); 
	.....
}

实现类也是泛型类,实现类和接口的泛型类型要一致

/**
 * 泛型接口
 * @param <T>
 */
public interface Generator<T> {
    T getKey();
}
/**
 * 泛型接口的实现类,是一个泛型类,
 * 那么要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识
 * @param <T>
 * @param <E>
 */
public class Pair<T,E> implements Generator<T> {

    private T key;
    private E value;

    public Pair(T key, E value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public T getKey() {
        return key;
    }

    public E getValue() {
        return value;
    }
}

实现类不是泛型类,接口要明确数据类型

/**
 * 实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型。
 */
public class Apple implements Generator<String> {
    @Override
    public String getKey() {
        return "hello generic";
    }
}

11.泛型方法
语法:

修饰符 <T,E, ...> 返回值类型 方法名(形参列表) { 方法体... }

说明:

  • public与返回值中间非常重要,可以理解为声明此方法为泛型方法。
  • 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  • < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

使用
泛型方法的使用是在调用方法的时候指定类型。
例子:

   public <E> E getProduct(ArrayList<E> list) {
        return list.get(random.nextInt(list.size()));

    }

我们在使用getProduct方法是给他传进去的是String就是String

  ProductGetter<Integer> productGetter=new ProductGetter<>();
        ArrayList<String> list=new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        String product = productGetter.getProduct(list);
        System.out.println(product);

System.out.println(product.getClass().getSimpleName());
获取变量的数据类型。
注意:泛型方法是独立于泛型类的,和类的泛型没有任何关系。
如果static方法要使用泛型能力,就必须使其成为泛型方法

 /**
     * 静态的泛型方法,采用多个泛型类型
     * @param t
     * @param e
     * @param k
     * @param <T>
     * @param <E>
     * @param <K>
     */
    public static <T,E,K> void printType(T t, E e, K k) {
        System.out.println(t + "\t" + t.getClass().getSimpleName());
        System.out.println(e + "\t" + e.getClass().getSimpleName());
        System.out.println(k + "\t" + k.getClass().getSimpleName());
    }

泛型方法与可变参数

 public static  <E> void print(E... e){
       for (E e1:e) {
           System.out.println(e1);
       }
    }

调用

 ProductGetter.print(1,2,3,4,5);

12.类型通配符
什么是类型通配符?????
类型通配符一般是使用"?"代替具体的类型实参。
所以,类型通配符是类型实参,而不是类型形参。
在这里插入图片描述
传入的泛型与方法需要传入的泛型不匹配怎么解决???这时就需要通配符了。。。
在这里插入图片描述
通配符支持传入任意类型实参。(?是类型实参,从输入中读取成实参,你是什么我就把我的实参设置成什么)
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值