项目

内容

这个作业属于哪个课程

https://www.cnblogs.com/nwnu-daizh/

这个作业的要求在哪里

https://www.cnblogs.com/nwnu-daizh/p/11815810.html

作业学习目标

  1. 理解泛型概念;
  2. 掌握泛型类的定义与使用;
  3. 掌握泛型方法的声明与使用;
  4. 掌握泛型接口的定义与实现;
  5. 了解泛型程序设计,理解其用途。

第一部分:总结第八章关于泛型程序设计理论知识

什么是泛型

什么是泛型程序设计?
  ●JDK 5.0中增加的泛型类型,是Java语言中类型安全的一次重要改进。

  泛型:也称参数化类型(parameterized type),就是在定义类、接口和方法时,通过类型参数指示将要处理的对象类型。( 如ArrayList类)

  ●泛型程序设计(Generic programming) :编写代码可以被很多不同类型的对象所重用。

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

参数化类型:

  • 把类型当作是参数一样传递
  • <数据类型> 只能是引用类型

相关术语:

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个称为ArrayList<E>泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

1、泛型类

泛型类的定义
    ●一个泛型类(generic class) 就是具有一个或多个类型变量的类,即创建用类型作为参数的类。

    如一个泛型类定义格式如下:  class Generics< K,V>

        其中K和V是类的可变类型参数。

泛型类的最基本写法:

 

例子:

  Pair类引入了一个类型变量T,用尖括号(<>)括起来,并放在类名的后面。

  泛型类可以有多个类型变量。例如:

    public class Pair<T, U> {…}

  类的类型变量用于指定方法的返回类型以及域、局部变量的类型。

泛型方法

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

泛型方法的声明
  泛型方法
    除了泛型类外,还可以只单独定义一一个方法作为泛型方法,用于指定方法参数或者返回值为泛型类型,留待方法调用时确定。

    泛型方法可以声明在泛型类中,也可以声明在普通类中。

泛型方法的基本用法例子:

/**
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/ public class 泛型方法 {
@Test
public void test() {
test1();
test2(new Integer(2));
test3(new int[3],new Object()); //打印结果
// null
// 2
// [I@3d8c7aca
// java.lang.Object@5ebec15
}
//该方法使用泛型T
public <T> void test1() {
T t = null;
System.out.println(t);
}
//该方法使用泛型T
//并且参数和返回值都是T类型
public <T> T test2(T t) {
System.out.println(t);
return t;
} //该方法使用泛型T,E
//参数包括T,E
public <T, E> void test3(T t, E e) {
System.out.println(t);
System.out.println(e);
}
}

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,例子:

 当实现泛型接口的类,未传入泛型实参时:

/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}

当实现泛型接口的类,传入泛型实参时:

/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}

泛型变量的限定

  1)定义泛型变量的上界
    public class NumberGeneric< T extends Number>
  2)泛型变量上界的说明
    上述声明规定了NumberGeneric类所能处理的
泛型变量类型需和Number有继承关系;

  3)extends 关键字所声明的上界既可以是一个类,也可以是一个接口;

  4)<T extends Bounding Type>表示T应该是绑定类型的子类型。

  5)一个类型变量或通配符可以有多个限定,限定类型用“&”分割。例如:

    < T extends Comparable & Serializable >

  6)定义泛型变量的下界
    List<? super CashCard> cards = new ArrayList<T>();
  7)泛型变量下界的说明
    通过使用super关键字可以固定泛型参数的类型为某种
类型的超类

    当希望为一个方法的参数限定类型时,通常可以使用下限通配符

    public static <T> void sort(T[] a,Comparator<? super T> c)
    {  ……
      }

通配符

通配符:
    “?”表明参数类型可以是任何一种类型,通配符一般有以下三种用法:

      1)单独的?,用于表示任何类型。

      2)? extends type, 表示带有上界。

      3)?supertype,表示带有下界。

通配符的类型限定
  Pair<? extends Employee>

  Pair<? super Manager>

无限定通配符

    Pair<?>

泛型类的约束与局限性

  ●不能用基本类型实例化类型参数
  ●运行时类型查询只适用于原始类型
  ●不能抛出也不能捕获泛型类实例
  ●参数化类型的数组不合法
  ●不能实例化类型变量
  ●泛型类的静态上下文中类型变量无效
  ●注意擦除后的冲突

泛型类型的继承规则

  1)Java中的数组是协变的( covariant)。
  例如: Integer扩 展了Number,那么在要求Number[]的地方完全可以传递或者赋予IntegerD,Number[]也是Integer[]的超类型。

  Employee是Manager的超类,因此可以将一个Manager[]数组赋给一个 类型为Employee[]的变量:

    Manager[] managerBuddies = {ceo, cfo};
    Employee[] employeeBuddies = managerBuddies;

  2)但这一原理不适用于泛型类型。
   例如:
    Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
    Pair<Employee> employeeBuddies = managerBuddies; /illgal
    employeeBuddies.setFirst(lowlyEmployee); .
  3)不允许这样做的理由:避免破坏要提供类型的安全泛型。

  4)Java中泛型类不具协变性。如果能够将List<Integer>赋给List<Number>。那么下面的代码就允许将非Integer的内容放入List<Integer>:

    List<Integer> li = new ArrayList<Integer>();
    List<Number> In = li; // illegal
    In.add(new Float(3.1415));
  5)泛型类可扩展或实现其它的泛型类。例如,
    ArrayList<T>类实现List<T>接口。这意味着,- 一个
    ArrayList<Manager>可以被转换为个List<Manager>。

泛型程序设计小总结

  1)定义一个泛型类时,在<>”内定义形式类型参数,例如:“class TestGeneric<K, V>”,其中“K",“V”不代表值,而是表示类型。

  2)实例化泛型对象的时候,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。例如:

    TestGeneric <String, String> t = new TestGeneric < String, String> ();

  3)泛型中<T extends Object> ,extends并不代表继承,它是类型范围限制。

  4)泛型类不是协变的。

2、实验内容和步骤

实验1 导入第8章示例程序,测试程序并进行代码注释。

测试程序1:

  编辑、调试、运行教材311312页代码,结合程序运行结果理解程序;

  在泛型类定义及使用代码处添加注释;

  掌握泛型类的定义及使用。

代码:

package pair1;

/**
* @version 1.00 2004-05-10
* @author Cay Horstmann
*/
public class Pair<T> //Pair类引用一个类型变量T,(T:泛型标识)
{ //实例化泛型类时,指定T的具体类型(类型变量可以指定方法的返回类型以及类变量和局部变量的类型)
//泛型类型的继承(可以参数化类型转换为原始类型)(如:Pair<Manager >是原始类型Pair的一个子类型。)
//类型变量定义在类上,方法中也可以使用
private T first;
private T second; public Pair() {
first = null;
second = null;
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
} public T getFirst() { //getFirst方法
return first;
}
public T getSecond() { //getSecond方法
return second;
}
public void setFirst(T newValue) { //setFirst方法
first = newValue;
}
public void setSecond(T newValue) { //setSecond方法
second = newValue;
}
}
package pair1;

/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest1
{ //简单的泛型方法
public static void main(String[] args)
{
String[] words = { "Mary", "had", "a", "little", "lamb" };
Pair<String> mm = ArrayAlg.minmax(words); //初始化一个字符串数组变量,并调用泛型方法
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
} class ArrayAlg
{ //泛型类
/**
* Gets the minimum and maximum of an array of strings.
* @param a an array of strings
* @return a pair with the min and max values, or null if a is null or empty
*/
public static Pair<String> minmax(String[] a)
{ //用泛型Pair类,返回值为实例化的类对象
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];//若比较结果大于0,则表明此时的min不是最小的,将新的字符串存进min
if (max.compareTo(a[i]) < 0) max = a[i];//和上面min的 比较方法一样
}
return new Pair<>(min, max);//返回新的Pair类对象
}
}

运行结果:

测试程序2:

  编辑、调试运行教材315 PairTest2,结合程序运行结果理解程序;

  在泛型程序设计代码处添加相关注释;

  了解泛型方法、泛型变量限定的定义及用途。

代码:

package pair2;

/**
* @version 1.00 2004-05-10
* @author Cay Horstmann
*/
//Pair泛型类
public class Pair<T> //Pair类引用一个类型变量T,(T:泛型标识)
{
private T first;
private T second; public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; }
public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
package pair2;

import java.time.*;

/**
* @version 1.02 2015-06-21
* @author Cay Horstmann
*/
public class PairTest2
{
public static void main(String[] args)
{
LocalDate[] birthdays =
{
LocalDate.of(1906, 12, 9), // G. Hopper
LocalDate.of(1815, 12, 10), // A. Lovelace
LocalDate.of(1903, 12, 3), // J. von Neumann
LocalDate.of(1910, 6, 22), // K. Zuse
};
Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
//在Pair类中定义对象mm和一个LocalDate类的birthdays数组
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
} class ArrayAlg
{ //泛型类ArrayAlg
/**
Gets the minimum and maximum of an array of objects of type T.
@param a an array of objects of type T
@return a pair with the min and max values, or null if a is null or empty
*/
public static <T extends Comparable> Pair<T> minmax(T[] a)
//将T限制为 实现了Comparable接口的类,则泛型的minmax方法只能被实现了Comparable接口的类的数组调用
//使用extends关键字,定义泛型变量的上界,调用Comparable接口
{
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];//若比较结果大于0,则表明此时的min不是最小的,将新的字符串存进min
if (max.compareTo(a[i]) < 0) max = a[i];//和上面min的 比较方法一样
}
return new Pair<>(min, max);//返回新的Pair类对象
}
}

运行结果:

测试程序3:

  用调试运行教材335 PairTest3,结合程序运行结果理解程序;

  了解通配符类型的定义及用途。

代码:

package pair3;

import java.time.*;

public class Employee
{ //定义属性
private String name;
private double salary;
private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day)//构造器
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
} public String getName() //getName访问器
{
return name;
} public double getSalary() //getSalary访问器
{
return salary;
} public LocalDate getHireDay() //getHireDay访问器
{
return hireDay;
} public void raiseSalary(double byPercent) //调用raiseSalary方法完成涨工资计算
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
package pair3;

public class Manager extends Employee
{
private double bonus;//属性 /**
@param name the employee's name
@param salary the salary
@param year the hire year
@param month the hire month
@param day the hire day
*/
public Manager(String name, double salary, int year, int month, int day)//构造器
{
super(name, salary, year, month, day);
bonus = 0;
} public double getSalary() //访问器
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
} public void setBonus(double b) //更改器
{
bonus = b;
} public double getBonus() //访问器
{
return bonus;
}
}
package pair3;

/**
* @version 1.00 2004-05-10
* @author Cay Horstmann
*/
public class Pair<T> //Pair类引入了一个类型变量T
{
private T first;
private T second; public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; }
public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
package pair3;

/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest3
{
public static void main(String[] args)
{
var ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);
var cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);
var buddies = new Pair<Manager>(ceo, cfo);
printBuddies(buddies); ceo.setBonus(1000000);
cfo.setBonus(500000);
Manager[] managers = { ceo, cfo }; var result = new Pair<Employee>();
minmaxBonus(managers, result);
System.out.println("first: " + result.getFirst().getName()
+ ", second: " + result.getSecond().getName());
maxminBonus(managers, result);
System.out.println("first: " + result.getFirst().getName()
+ ", second: " + result.getSecond().getName());
} public static void printBuddies(Pair<? extends Employee> p)
//?是通配符,表明参数的类型是上界为Employee的任何一种类型
{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
} public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
//?是通配符,表明参数的类型是下界为manager的任何一种类型
{
if (a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
} public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swapHelper(result); // OK--swapHelper captures wildcard type
}
// can't write public static <T super manager> . . .
} class PairAlg
{
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
} public static void swap(Pair<?> p) { swapHelper(p); } public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}

运行结果:

实验2结对编程练习,将程序提交到PTA(2019面向对象程序设计基础知识测试题

1 编写一个泛型接口GeneralStack,要求类方法对任何引用类型数据都适用。GeneralStack接口中方法如下:

2定义GeneralStack的类ArrayListGeneralStack要求:

  类内使用ArrayList对象存储堆栈数据,名为list;

  方法: public String toString()//代码为return list.toString();

  代码中不要出现类型不安全的强制转换。

3定义Car类,类的属性有:

  private int id;

  private String name;

方法:Eclipse自动生成setter/getter,toString方法。

4main方法要求

  输入选项,有quit, Integer, Double, Car 4个选项。如果输入quit,程序直接退出。否则,输入整数m与n。m代表入栈个数,n代表出栈个数。然后声明栈变量stack。

   输入Integer,打印Integer Test。建立可以存放Integer类型的ArrayListGeneralStack。入栈m次,出栈n次。打印栈的toString方法。最后将栈中剩余元素出栈并累加输出。

  输入Double ,打印Double Test。剩下的与输入Integer一样。

  输入Car,打印Car Test。其他操作与Integer、Double基本一样。只不过最后将栈中元素出栈,并将其name依次输出。

特别注意:如果栈为空,继续出栈,返回null

输入样例

输出样例

提交结果:

代码:

package F2;

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner ss=new Scanner(System.in);
while(true)
{
String c=ss.nextLine();
if(c.equals("Integer"))
{
System.out.println("Integer Test");
int pushStack=ss.nextInt();
int popStack=ss.nextInt();
ArrayListGeneralStack Stack = new ArrayListGeneralStack();
for(int i=0;i<pushStack;i++)
{
System.out.print("push:"+Stack.push(ss.nextInt()));
Stack.push(ss.nextInt());
}
for(int i=0;i<popStack;i++)
{
System.out.println("pop:"+Stack.pop());
}
System.out.println(Stack.toString()); int sum=0;
int size=Stack.size();
for(int k=0;k<size;k++)
{
sum+=(int)Stack.pop();
}
System.out.println("sum="+sum);
System.out.println("interface GeneralStack");
}
else if(c.equals("Double"))
{
System.out.println("Double Test");
int pushStack=ss.nextInt();
int popStack=ss.nextInt();
ArrayListGeneralStack Stack = new ArrayListGeneralStack();
for(int i=0;i<pushStack;i++)
{
System.out.print("push:"+Stack.push(ss.nextDouble()));
Stack.push(ss.nextDouble());
}
for(int i=0;i<popStack;i++)
{
System.out.println("pop:"+Stack.pop());
}
System.out.println(Stack.toString()); double sum=0;
int size=Stack.size();
for(int k=0;k<size;k++)
{
sum+=(double)Stack.pop();
}
System.out.println("sum="+sum); System.out.println("interface GeneralStack");
}
else if(c.equals("Car"))
{
System.out.println("Car Test");
int pushStack=ss.nextInt();
int popStack=ss.nextInt();
ArrayListGeneralStack Stack = new ArrayListGeneralStack();
for(int i=0;i<pushStack;i++)
{
int id=ss.nextInt();
String name=ss.next();
Car car=new Car(id,name);
System.out.println("push:"+Stack.push(car));
}
for(int i=0;i<popStack;i++)
{
System.out.println("pop:"+Stack.pop());
}
System.out.println(Stack.toString()); int size=Stack.size();
for(int k=0;k<size;k++)
{
Car car=(Car)Stack.pop();
System.out.println(car.getName());
} System.out.println("interface GeneralStack");
}
else if(c.equals("quit")){
System.out.println("程序终止");
break;
}
}
ss.close();
}
}
package F2;

import java.util.ArrayList;

class ArrayListGeneralStack implements GeneralStack<Object>{
ArrayList<Object> list=new ArrayList<>(); @Override
public Object push(Object item) {
if(item!=null)
{
list.add(item);
return item;
}else {
return null;
}
} @Override
public Object pop() {
if(list.size()==0)
{
return null;
}else {
return list.remove(list.size()-1);
} } @Override
public Object peek() {
if(list.size()==0)
{
return null;
}else {
return list.get(list.size()-1);
} } @Override
public boolean empty() {
if(list.size()==0)
{
return true;
}else {
return false;
}
} @Override
public int size() {
return list.size();
} public String toString()
{
return list.toString();
}
}
package F2;

public class Car {
private int id;
private String name; public Car(int id, String name) {
this.id = id;
this.name = name;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Car [id=" + id + ", name=" + name + "]";
} }
package F2;

interface GeneralStack<T>{
public T push(T item); //如item为null,则不入栈直接返回null。
public T pop(); //出栈,如为栈为空,则返回null。
public T peek(); //获得栈顶元素,如为空,则返回null.
public boolean empty();//如为空返回true
public int size(); //返回栈中元素数量
}

实验总结:(15分)

  理解掌握了泛型类的定义与使用,泛型方法的声明与使用;泛型接口的定义与实现;使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强行类型转换的代码具有更好的安全性和可读性。通过这次对泛型类的学习,我基本掌握泛型程序设计的“泛型”到底指的是什么,我希望在以后的学习过程当中能够越来越好。

 

最新文章

  1. js流程控制题——如何实现一个LazyMan
  2. jquery 模拟 alert 手机,pc,平板 3合一
  3. 如果我可以重新学习iOS开发(转)
  4. 关于Repeater嵌套绑定的问题
  5. Oracle的使用
  6. HTML5[5]:在移动端禁用长按选中文本功能
  7. 详细的图文教程来实现 eclipse环境下如何配置tomcat,并且把项目部署到Tomcat服务器上
  8. Syntax error, annotations are only available if source level is 1.5
  9. Android动画的使用总结
  10. jquery控制css的display(控制元素的显示与隐藏)
  11. laravel code bright
  12. Deep Learning(深度学习)学习笔记整理系列之(六)
  13. cocos2dx CCLabelTTF自己定义字体的使用
  14. Java 垃圾回收机制(早期版本)
  15. 测试BUG记录模板(供参考)
  16. C# 以管理员权限删除文件
  17. vue_过渡_动画
  18. win10 uwp 渲染原理 DirectComposition 渲染
  19. SQL SERVER 一组数据按规律横着放置,少则补空,如人员按一进一出的规律,进出为一组,缺少的补null
  20. sql数据库之多库查询

热门文章

  1. 【西北师大-2108Java】第三次作业成绩汇总
  2. markdown 编辑格式
  3. 【2019.7.22 NOIP模拟赛 T1】麦克斯韦妖(demon)(质因数分解+DP)
  4. 记录错误or日记(更新中)
  5. sql server 按照字段分组 重新设置组序号
  6. 数据仓库003 - 复习Linux shell命令 - 用户用户组 sudo 权限 du-sh find 
  7. 源码详解系列(五) ------ C3P0的使用和分析(包括JNDI)
  8. 造轮子ArrayList
  9. 阿里云CentOS7.x安装nodejs及pm2
  10. 使用jstack排查多线程死锁、阻塞