13. 泛型和枚举

html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video { margin: 0; padding: 0; border: 0 }
body { font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 1.6; color: #333; background-color: #fff; padding: 20px; max-width: 960px; margin: 0 auto }
body>*:first-child { margin-top: 0 !important }
body>*:last-child { margin-bottom: 0 !important }
p,blockquote,ul,ol,dl,table,pre { margin: 15px 0 }
h1,h2,h3,h4,h5,h6 { margin: 20px 0 10px; padding: 0; font-weight: bold }
h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code { font-size: inherit }
h1 { font-size: 28px; color: #000 }
h2 { font-size: 24px; border-bottom: 1px solid #ccc; color: #000 }
h3 { font-size: 18px }
h4 { font-size: 16px }
h5 { font-size: 14px }
h6 { color: #777; font-size: 14px }
body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child { margin-top: 0; padding-top: 0 }
a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6 { margin-top: 0; padding-top: 0 }
h1+p,h2+p,h3+p,h4+p,h5+p,h6+p { margin-top: 10px }
a { color: #4183C4; text-decoration: none }
a:hover { text-decoration: underline }
ul,ol { padding-left: 30px }
ul li>:first-child,ol li>:first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type { margin-top: 0px }
ul ul,ul ol,ol ol,ol ul { margin-bottom: 0 }
dl { padding: 0 }
dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px }
dl dt:first-child { padding: 0 }
dl dt>:first-child { margin-top: 0px }
dl dt>:last-child { margin-bottom: 0px }
dl dd { margin: 0 0 15px; padding: 0 15px }
dl dd>:first-child { margin-top: 0px }
dl dd>:last-child { margin-bottom: 0px }
pre,code,tt { font-size: 12px; font-family: Consolas, "Liberation Mono", Courier, monospace }
code,tt { margin: 0 0px; padding: 0px 0px; white-space: nowrap; border: 1px solid #eaeaea; background-color: #f8f8f8 }
pre>code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent }
pre { background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px }
pre code,pre tt { background-color: transparent; border: none }
kbd { background-color: #DDDDDD; background-image: linear-gradient(#F1F1F1, #DDDDDD); background-repeat: repeat-x; border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; border-style: solid; border-width: 1px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 10px; padding: 1px 4px }
blockquote { border-left: 4px solid #DDD; padding: 0 15px; color: #777 }
blockquote>:first-child { margin-top: 0px }
blockquote>:last-child { margin-bottom: 0px }
hr { clear: both; margin: 15px 0; height: 0px; overflow: hidden; border: none; background: transparent; border-bottom: 4px solid #ddd; padding: 0 }
table th { font-weight: bold }
table th,table td { border: 1px solid #ccc; padding: 6px 13px }
table tr { border-top: 1px solid #ccc; background-color: #fff }
table tr:nth-child(2n) { background-color: #f8f8f8 }
img { max-width: 100% }

1. 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

假定我们有这样一个需求:写一个排序方法,能够对整形数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。

1.1 为什么需要泛型

1.1.1 没有泛型会怎么样

先看下面的代码:

List list = new ArrayList();
list.add("RIMI");
list.add(100);
for (int i = 0; i < list.size(); i++) {
  String name = (String) list.get(i); //取出Integer时,运行时出现异常
System.out.println("name:" + name);
}

本例向list类型集合中加入了一个字符串类型的值和一个Integer类型的值(这样是合法的,因为此时list默认的类型为Object类型)。

在循环中,由于忘记了之前添加了Integer类型的值或其他原因,运行时会出现java.lang.ClassCastException。为了解决这个问题,泛型应运而生。

1.1.2 泛型的使用

Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。

只要在上例中将第1行代码改成如下形式,那么就会在编译list.add(100)时报错。

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

通过List,直接限定了list集合中只能含有String类型的元素,从而在上例中的第6行中,无须进行强制类型转换,因为集合能够记住其中元素的类型信息,编译器已经能够确认它是String类型了。

1.1.3 泛型只在编译阶段有效

ArrayList<String> a = new ArrayList<String>();
ArrayList b = new ArrayList();
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2); //true

上面程序的输出结果为true。因为所有反射的操作都是在运行时的,既然为true,就证明了编译之后,程序会采取去泛型化的措施。

也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,成功编译过后的class文件中是不包含任何泛型信息的。

1.2 泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的< E >)。 (java中E,T,?的区别?)
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

下面的例子演示了如何使用泛型方法打印不同字符串的元素:

public class GenericMethodTest
{
   // 泛型方法 printArray
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素
         for ( E element : inputArray ){
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { ‘H‘, ‘E‘, ‘L‘, ‘L‘, ‘O‘ };

        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组

        System.out.println( "\n双精度型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组

        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    }
}

结果为:

整型数组元素为:
1 2 3 4 5 

双精度型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O

1.3 泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

示例:

public class Box<T> {

  private T t;

  public void add(T t) {
    this.t = t;
  }

  public T get() {
    return t;
  }

  public static void main(String[] args) {
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();

    integerBox.add(new Integer(10));
    stringBox.add(new String("睿峰科技"));

    System.out.printf("整型值为 :%d\n\n", integerBox.get());
    System.out.printf("字符串为 :%s\n", stringBox.get());
  }
}

运行结果:

整型值为 :10

字符串为 :睿峰科技

1.4 类型通配符

1、类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。

public class GenericTest {

    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        getData(name);
        getData(age);
        getData(number);

   }

   public static void getData(List<?> data) {
      System.out.println("data :" + data.get(0));
   }
}

输出结果为:

data :icon
data :18
data :314

解析: 因为getDate()方法的参数是List类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用

2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

public class GenericTest {

    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        //getUperNumber(name);//1
        getUperNumber(age);//2
        getUperNumber(number);//3

   }

   public static void getData(List<?> data) {
      System.out.println("data :" + data.get(0));
   }

   public static void getUperNumber(List<? extends Number> data) {
          System.out.println("data :" + data.get(0));
       }
}

输出结果:

data :18
data :314

解析: 在(//1)处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错

3、类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如Objec类型的实例。

<? extends T>和<? super T>的区别
<? extends T>表示该通配符所代表的类型是T类型的子类。
<? super T>表示该通配符所代表的类型是T类型的父类。

这也被称为泛型的上下边界

1.5 泛型的好处

  1. 类型安全。

    通过知道泛型定义的变量类型限制,编译器可以更有效地提高Java程序的类型安全。

  2. 消除强制类型转换。

    消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。所有的强制转换都是自动和隐式的。

  3. 提高性能。
    Lits list1 = new ArrayList();
    list1.add("RIMI");
    String str1 = (String)list1.get(0);  
    
    List<String> list2 = new ArrayList<String>();
    list2.add("RIMI");
    String str2 = list2.get(0);
    

    对于上面的两段程序,由于泛型所有工作都在编译器中完成,javac编译出来的字节码是一样的(只是更能确保类型安全),那么何谈性能提升呢?是因为在泛型的实现中,编译器将强制类型转换插入生成的字节码中,但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来了可能。

1.6 List与List<?>

  1. List实际上也是List
  2. 而List<?>表示具有某种特定类型的非原生List,只是我们不知道那种类型是什么。

练习:

用TreeSet装入学生类型和工人类型两种类型,两种类型都要进行排序。要求只有一个比较器。

2. 枚举

// 枚举定义
public enum Color {//enum是计算机编程语言中的一种数据类型。枚举类型:在实际问题中,有些变量的取值被限定在一个有限的范围内。
     RED, GREEN, BLANK, YELLOW
}
// 枚举使用
public static void main(String[] args) {
    System.out.println( isRed( Color.BLANK ) ) ;  //结果: false
    System.out.println( isRed( Color.RED ) ) ;    //结果: true

}
// 枚举值的使用
static boolean isRed( Color color ){
    if ( Color.RED.equals( color )) {
        return true ;
    }
    return false ;
}

===================================================
public enum Color {
    RED, GREEN, BLANK, YELLOW
}

public static void main(String[] args) {
    showColor(Color.RED);
}

static void showColor(Color color) {
    switch (color) {
    case BLANK:
        System.out.println(color);
        break;
    case RED:
        System.out.println(color);
        break;
    default:
        System.out.println(color);
        break;
    }
}

===================================================
//自定义函数
public enum Color {
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    private String name;
    private int index;
    private Color(String name, int index) {
        this.name = name;
        this.index = index;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getIndex() {
        return index;
    }
    public void setIndex(int index) {
        this.index = index;
    }
}

public static void main(String[] args) {
    // 输出某一枚举的值
    System.out.println(Color.RED.getName());
    System.out.println(Color.RED.getIndex());
    // 遍历所有的枚举
    for (Color color : Color.values()) {
        System.out.println(color + "  name: " + color.getName() + "  index: " + color.getIndex());
    }
}
时间: 2024-08-27 22:10:14

13. 泛型和枚举的相关文章

java JDK8 学习笔记——第18章 自定义泛型、枚举与注释

第十八章 自定义泛型.枚举与注释 18.1 自定义泛型 泛型定义: (1)仅定义在方法上的泛型语法 (2)用来限制泛型可用类型的extends与super关键字(3)?类型通配字符的使用 18.1.1 使用extends与? 1.若extends之后指定了类与接口,想再指定其他接口,可以使用&连接. 2.如果B是A的子类,而Node< B>可视为一种Node< A>,则称Node具有共变性或有弹性的.Java泛型不具有共变性,可以使用类型通配字符?与extends来声明变量

JAVA学习-Java新特性(泛型、枚举、Annotation)

所谓的Java新特性现在都是指从JDK 1.5之后开始的,例如,在前面已经学习过两个新特性:switch支持String判断(JDK 1.7提供的).自动装箱和拆箱.可变参数.foreach.静态导入.泛型.枚举.Annotation. 对于所有的新特性,我的个人建议:有些新特性你今天一定是不知道怎么用的,我们今天只是来看一下这些语法,至于使用方面,慢慢来观察. 1.可变参数(理解) 如果说现在有这样一个要求,要求实现整数的加法操作,并且方法可以接收任意多个整型数据一起实现加法操作. 如果按照传

W6-junit、泛型、枚举、增强for、可变参数、反射[JavaWeb]

1.单元测试Junit 1.1 测试对象是一个类中的方法 1.2 junit不是javase的部分,使用时需要导入jar包,myeclipse自带jar包   1.3 单元测试方法的时候,方法命名规则 public void 方法名(){} 1.4 使用注解方式运行,在方法的上面@Test package cn.itcast.test02; import org.junit.Test; public class testDemo { //测试方法 @Test public void testAd

泛型和枚举

泛型: 用于解决安全问题,是一个安全机制. 泛型格式:通过<>定义操作的引用数据类型. 使用:当类中要操作的引用数据类型不确定的时候,早起定义Object来完成扩展,现在定义泛型类来扩展. 泛型类定义的泛型,在整个类中有效,如果被方法是用,那么泛型类对象的明确要操作的具体类型.所有方法类型不确定,那么可以将泛型定义在方法上. public <T> T  print(T t){//<T>定义泛型     T返回类型 return t; } 静态泛型方法: 1.静态方法不可

java.基础、集合、枚举、泛型、

1.myeclipse的安装和使用 * eclipse:是一个免费的开发工具 * myeclipse:是一个收费的插件,破解myeclipse, ** 安装目录的要求: 不能有中文和空格 ** 安装完成之后,选择一个工作空间 ,这个工作空间不能有中文和空格 * 破解myeclipse ** 运行run.bat文件,但是运行之前,必须要安装jdk,通过配置环境变量 * myeclipse的使用 * 创建一个工程 - 类型 java project web project - 选择依赖的jdk,可以

枚举和泛型

1.枚举类型 JDK1.5中新增了枚举类型与泛型. 枚举类型可以取代以往常量的定义方式,即将常量封装在类或接口中,此外,它还提供了安全检查功能. 枚举类型本质上还是以类的形式存在. enum是定义枚举类型的关键字. 例如:在项目中创建Constants接口,在接口中定义敞亮的常规方式: public interface Constants{ public static final int Constants_A=1; public static final int Constants_B=12;

第17章 枚举类型与泛型

1.枚举类型 JDK1.5中新增了枚举类型与泛型. 枚举类型可以取代以往常量的定义方式,即将常量封装在类或接口中,此外,它还提供了安全检查功能. 枚举类型本质上还是以类的形式存在. enum是定义枚举类型的关键字. 例如:在项目中创建Constants接口,在接口中定义敞亮的常规方式: public interface Constants{ public static final int Constants_A=1; public static final int Constants_B=12;

泛型概念及其特性

1.泛型是JDK1.5引入的新特性,也是最重要的一个特性2.泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的3.泛型的原理就是"类型的参数化",即把类型看做参数.也就是说把所要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样. 4.简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息5.泛型可以提高代码的扩展性和重用性 6.泛型的作用及其引出: 如果我们需要产生多个对象,每个对象的逻辑完全相同,知识对象内的成员变量的类型

Item 30 用enum代替int常量类型枚举,string常量类型枚举

1.用枚举类型替代int枚举类型和string枚举类型 public class Show { // Int枚举类型 // public static final int APPLE_FUJI = 0; // public static final int APPLE_PIPPIN = 1; // public static final int APPLE_GRANNY_SMITH = 2; public enum Apple { FUJI, PIPPIN, GRANNY_SMITH } pub