J2SE 5.0中的泛型

J2SE 5.0中的泛型
作者:Budi Kurniawan
翻译: RR00
email:di_feng_ro@hotmail.com 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
英文原文地址:
http://www.onjava.com/pub/a/onjava/2005/07/06/generics.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43634_java_generics.html
关键词: java generics java5

摘要
       泛型是J2SE 5.0最重要的特性。他们让你写一个type(类或接口)和创建一个实例通过传递一个或多个引用类型。这个实例受限于只能作用于这些类型。比如,在java 5,java.util.List 已经被泛化。当建立一个list对象时,你通过传递一个java类型建立一个List实例,此list实例只能作用于所传递的类型。这意味着如果你传递一个String ,此List实例只能拥有String对象;如果你传递一个Integer,此实例只能存贮Integer对象。除了创建参数化的类型,你还能创建参数化的函数。
     泛型的第一个好处是编译时的严格类型检查。这是集合框架最重要的特点。此外,泛型消除了绝大多数的类型转换。在JDK 5.0之前,当你使用集合框架时,你不得不进行类型转换。
     本文将教你如何操作泛型。它的第一部分是“没有泛型的日子”,先让我们回忆老版本JDK的不便。然后,举一些泛型的例子。在讨论完语法以及有界泛型的使用之后,文章最后一章将解释如何写泛型。
  

没有泛型的日子
     所有的java类都源自java.lang.Object,这意味着所有的JAVA对象能转换成Object。因此,在之前的JDK的版本中,很多集合框架的函数接受一个Object参数。所以,collections是一个能持有任何对象的多用途工具,但带来了不良的后果。

     举个简单的例子,在JDK 5.0的之前版本中,类List的函数add接受一个Object参数:

public boolean add(java.lang.Object element)



        所以你能传递任何类型给add。这是故意这么设计的。否则,它只能传递某种特定的对象,这样就会出现各种List类型,如,StringList, EmployeeList, AddressList等。
     add通过Object传递能带来好处,现在我们考虑get函数(返回List中的一个元素).如下是JDK 5之前版本的定义:

public java.lang.Object get(int index) throws IndexOutOfBoundsException



get返回一个Object.不幸的事情从此开始了.假如你储存了两个String对象在一个List中:

List stringList1 = new ArrayList();
stringList1.add("Java 5");
stringList1.add("with generics");



当你想从stringList1取得一个元素时,你得到了一个Object.为了操作原来的类型元素,你不得不把它转换为String。

String s1 = (String) stringList1.get(0);



但是,假如你曾经把一个非String对象加入stringList1中,上面的代码会抛出一个ClassCastException. 有了泛型,你能创建一个单一用途的List实例.比如,你能创建一个只接受String对象的List实例,另外一个实例只能接受Employee对象.这同样适用于集合框架中的其他类型.


泛型入门

   像一个函数能接受参数一样,一个泛型也能接受参数.这就是一个泛型经常被称为一个参数化类型的原因.但是不像函数用()传递参数,泛型是用<>传递参数的.声明一个泛型和声明一个普通类没有什么区别,只不过你把泛型的变量放在<>中.
     比如,在JDK 5中,你可以这样声明一个java.util.List :  List<E> myList;
E 称为类型变量.意味着一个变量将被一个类型替代.替代类型变量的值将被当作参数或返回类型.对于List接口来说,当一个实例被创建以后,E 将被当作一个add或别的函数的参数.E 也会使get或别的参数的返回值.下面是add和get的定义:

boolean add<E o>
E get(int index)



NOTE:一个泛型在声明或例示时允许你传递特定的类型变量: E.除此之外,如果E是个类,你可以传递子类;如果E是个接口,你可以传递实现接口的类;

-----------------------------译者添加--------------------
List<Number> numberList= new ArrayList<Number>();
   numberList.add(2.0);
   numberList.add(2);
-----------------------------译者添加--------------------


如果你传递一个String给一个List,比如:

List<String> myList;



那么mylist的add函数将接受一个String作为他的参数,而get函数将返回一个String.因为返回了一个特定的类型,所以不用类型转化了。

NOTE:根据惯例,我们使用一个唯一的大写字目表示一个类型变量。为了创建一个泛型,你需在声明时传递同样的参数列表。比如,你要想创建一个ArrayList来操作String ,你必须把String放在<>中。如:

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



再比如,java.util.Map 是这么定义的:

public interface Map<K,V>



K用来声明map键(KEY)的类型而V用来表示值(VALUE)的类型。put和values是这么定义的:

V put(K key, V value)
Collection<V> values()



NOTE:一个泛型不准直接的或间接的是java.lang.Throwable的子类。因为异常是在运行时抛出的,所以它不可能预言什么类型的异常将在编译时抛出.

列表1的例子将比较List在JDK 1.4 和JDK1.5的不同

package com.brainysoftware.jdk5.app16;
import java.util.List;
import java.util.ArrayList;

public class GenericListTest {
  public static void main(String[] args) {
    // in JDK 1.4
    List stringList1 = new ArrayList();
    stringList1.add("Java 1.0 - 5.0");
    stringList1.add("without generics");
    // cast to java.lang.String
    String s1 = (String) stringList1.get(0);
    System.out.println(s1.toUpperCase());

    // now with generics in JDK 5
    List<String> stringList2 = new ArrayList<String>();
    stringList2.add("Java 5.0");
    stringList2.add("with generics");
    // no need for type casting
    String s2 = stringList2.get(0);
    System.out.println(s2.toUpperCase());
  }
}



在列表1中,stringList2是个泛型。声明List<String>告诉编译器List的实例能接受一个String对象。当然,在另外的情况中,你能新建能接受各种对象的List实例。注意,当从List实例中返回成员元素时,不需要对象转化,因为他返回的了你想要的类型,也就是String.

NOTE:泛型的类型检查(type checking)是在编译时完成的.

      最让人感兴趣的事情是,一个泛型是个类型并且能被当作一个类型变量。比如,你想你的List储存lists of Strings,你能通过把List<String>作为他的类型变量来声明List。比如:

List<List<String>> myListOfListsOfStrings;



要从myList中的第一个List重新取得String,你可以这么用:

String s = myListOfListsOfStrings.get(0).get(0);



下一个列表中的ListOfListsTest类示范了一个List(命名为listOfLists)接受一个String List作为参数。

package com.brainysoftware.jdk5.app16;
import java.util.ArrayList;
import java.util.List;
public class ListOfListsTest {
  public static void main(String[] args) {
    List<String> listOfStrings = new ArrayList<String>();
    listOfStrings.add("Hello again");
    List<List<String>> listOfLists = new ArrayList<List<String>>();
    listOfLists.add(listOfStrings);
    String s = listOfLists.get(0).get(0);
    System.out.println(s); // prints "Hello again"
  }
}



另外,一个泛型接受一个或多个类型变量。比如,java.util.Map有两个类型变量s。第一个定义了键(key)的类型,第二个定义了值(value)的类型。下面的例子讲教我们如何使用个一个泛型Map.

package com.brainysoftware.jdk5.app16;
import java.util.HashMap;
import java.util.Map;
public class MapTest {
  public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    String value1 = map.get("key1");
  }
}



在这个例子中,重新得到一个key1代表的String值,我们不需要任何类型转换。

没有参数的情况下使用泛型

    既然在J2SE 5.0中收集类型已经泛型化,那么,原来的使用这些类型的代码将如何呢?很幸运,他们在JAVA 5中将继续工作,因为你能使用没有参数的泛型。比如,你能继续像原来一样使用List接口,正如下面的例子一样。

List stringList1 = new ArrayList();
stringList1.add("Java 1.0 - 5.0");
stringList1.add("without generics");
String s1 = (String) stringList1.get(0);



一个没有任何参数的泛型被称为原型(raw type)。它意味着这些为JDK1.4或更早的版本而写的代码将继续在java 5中工作。

尽管如此,一个需要注意的事情是,JDK5编译器希望你使用带参数的泛型。否则,编译器将提示警告,因为他认为你可能忘了定义类型变量s。比如,编译上面的代码的时候你会看到下面这些警告,因为第一个List被认为是原型。

Note: com/brainysoftware/jdk5/app16/GenericListTest.java
        uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

当你使用原型时,如果你不想看到这些警告,你有几个选择来达到目的:
1.编译时带上参数-source 1.4
2.使用@SupressWarnings("unchecked")注释
3.更新你的代码,使用List<Object>. List<Object>的实例能接受任何类型的对象,就像是一个原型List。然而,编译器不会报错。

使用 ? 通配符
   前面提过,如果你声明了一个List<aType>, 那么这个List对aType起作用,所以你能储存下面这些类型的对象:
1.一个aType的实例
2.它的子类的实例(如果aType是个类)
3.实现aType接口的类实例(如果aType是个接口)
但是,请注意,一个泛型本身是个JAVA类型,就像java.lang.String或java.io.File一样。传递不同的类型变量给泛型可以创建不同的JAVA类型。比如,下面例子中list1和list2引用了不同的类型对象。

List<Object> list1 = new ArrayList<Object>();
List<String> list2 = new ArrayList<String>();



list1指向了一个类型变量s为java.lang.Objects 的List而list2指向了一个类型变量s为String 的List。所以传递一个List<String>给一个参数为List<Object>的函数将导致compile time错误。下面列表可以说明:

package com.brainysoftware.jdk5.app16;
import java.util.ArrayList;
import java.util.List;

public class AllowedTypeTest {
  public static void doIt(List<Object> l) {
  }
  public static void main(String[] args) {
    List<String> myList = new ArrayList<String>();
    // 这里将产生一个错误
    doIt(myList);
  }
}


上面的代码无法编译,因为你试图传递一个错误的类型给函数doIt。doIt的参数是List<Object>二你传递的参数是List<String>。
可以使用 ? 通配符解决这个难题。List<?> 意味着一个对任何对象起作用的List。所以,doIt可以改为:

public static void doIt(List<?> l) {}



    在某些情况下你会考虑使用 ? 通配符。比如,你有一个printList函数,这个函数打印一个List的所有成员,你想让这个函数对任何类型的List起作用时。否则,你只能累死累活的写很多printList的重载函数。下面的列表引用了使用 ? 通配符的printList函数。

package com.brainysoftware.jdk5.app16;
import java.util.ArrayList;
import java.util.List;

public class WildCardTest {

  public static void printList(List<?> list) {
    for (Object element : list) {
      System.out.println(element);
    }
  }
  public static void main(String[] args) {
    List<String> list1 = new ArrayList<String>();
    list1.add("Hello");
    list1.add("World");
    printList(list1);

    List<Integer> list2 = new ArrayList<Integer>();
    list2.add(100);
    list2.add(200);
    printList(list2);
  }
}



这些代码说明了在printList函数中,List<?>表示各种类型的List对象。然而,请注意,在声明的时候使用 ? 通配符是不合法的,像这样:

List<?> myList = new ArrayList<?>(); // 不合法



如果你想创建一个接收任何类型对象的List,你可以使用Object作为类型变量,就像这样:

List<Object> myList = new ArrayList<Object>();



在函数中使用界限通配符
在之前的章节中,你学会了通过传递不同的类型变量s来创建不同JAVA类型的泛型,但并不考虑类型变量s之间的继承关系。在很多情况下,你想一个函数有不同的List参数。比如,你有一个函数getAverage,他返回了一个List中成员的平均值。然而,如果你把List<Number>作为getAverage的参数,你就没法传递List<Integer> 或List<Double>参数,因为List<Number>和List<Integer> 和List<Double>不是同样的类型。

你能使用原型或使用通配符,但这样无法在编译时进行安全类型检查,因为你能传递任何类型的List,比如List<String>的实例。你可以使用List<Number>作为参数,但是你就只能传递List<Number>给函数。但这样就使你的函数功能减少,因为你可能更多的时候要操作List<Integer>或List<Long>,而不是List<Number>。

J2SE5.0增加了一个规则来解决了这种约束,这个规则就是允许你定义一个上界(upper bound) 类型变量.在这种方式中,你能传递一个类型或它的子类。在上面getAverage函数的例子中,你能传递一个List<Number>或它的子类的实例,比如List<Integer> or List<Float>。

使用上界规则的语法这么定义的:GenericType<? extends upperBoundType>. 比如,对getAverage函数的参数,你可以这么写List<? extends Number>. 下面例子说明了如何使用这种规则。

package com.brainysoftware.jdk5.app16;
import java.util.ArrayList;
import java.util.List;
public class BoundedWildcardTest {
  public static double getAverage(List<? extends Number> numberList)
  {
    double total = 0.0;
    for (Number number : numberList)
      total += number.doubleValue();
    return total/numberList.size();
  }

  public static void main(String[] args) {
    List<Integer> integerList = new ArrayList<Integer>();
    integerList.add(3);
    integerList.add(30);
    integerList.add(300);
    System.out.println(getAverage(integerList)); // 111.0
    List<Double> doubleList = new ArrayList<Double>();
    doubleList.add(3.0);
    doubleList.add(33.0);
    System.out.println(getAverage(doubleList)); // 18.0
  }
}


由于有了上界规则,上面例子中的getAverage函数允许你传递一个List<Number> 或一个类型变量是任何java.lang.Number子类的List。

下界规则
关键字extends定义了一个类型变量的上界。通过使用super关键字,我们可以定义一个类型变量的下界,尽管使用的情况不多。比如,如果一个函数的参数是List<? super Integer>,那么意味着你可以传递一个List<Integer>的实例或者任何java.lang.Integer的超类(superclass)。

创建泛型

前面的章节主要说明了如何使使用泛型,特别是集合框架中的类。现在我们开始学习如何写自己的泛型。

基本上,除了声明一些你想要使用的类型变量s外,一个泛型和别的类没有什么区别。这些类型变量s位于类型后面的<>中。比如,下面的Point就是个泛型。一个Point对象代表了一个系统中的点,它有横坐标和纵坐标。通过使Point泛型化,你能定义一个点实例的精确程度。比如,如果一个Point对象需要非常精确,你就把Double作为类型变量。否则,Integer 就够了。

package com.brainysoftware.jdk5.app16;
public class Point<T> {
  T x;
  T y;
  public Point(T x, T y) {
    this.x = x;
    this.y = y;
  }
  public T getX() {
    return x;
  }
  public T getY() {
    return y;
  }
  public void setX(T x) {
    this.x = x;
  }
  public void setY(T y) {
    this.y = y;
  }
}



在这个例子中,T是Point的类型变量 。T是getX和getY的返回值类型,也是setX和setY的参数类型。此外,构造函数结合两个T参数。
使用point类就像使用别的类一样。比如,下面的例子创建了两个Point对象:ponint1和point2。前者把Integer作为类型变量,而后者把Double作为类型变量。

Point<Integer> point1 = new Point<Integer>(4, 2);
point1.setX(7);
Point<Double> point2 = new Point<Double>(1.3, 2.6);
point2.setX(109.91);



总结
泛型使代码在编译时有了更严格的类型检查。特别是在集合框架中,泛型有两个作用。第一,他们增加了对集合类型在编译时的类型检查,所以集合类所能持有的类型对传递给它的参数类型起了限制作用。比如你创建了一个持有strings的java.util.List实例,那么他就将不能接受Integers或别的类型。其次,当你从一个集合中取得一个元素时,泛型消除了类型转换的必要。
泛型能够在没有类型变量的情况下使用,比如,作为原型。这些措施让Java 5之前的代码能够运行在JRE 5中。但是,对新的应用程序,你最好不要使用原型,因为以后Java可能不支持他们。

你已经知道通过传递不同类型的类型变量给泛型可以产生不同的JAVA类型。就是说List<String>和List<Object>的类型是不同的。尽管String是java.lang.Object。但是传递一个List<String>给一个参数是List<Object>的函数会参数会产生编译错误(compile error)。函数能用 ? 通配符使其接受任何类型的参数。List<?> 意味着任何类型的对象。
最后,你已经看到了写一个泛型和别的一般JAVA类没有什么区别。你只需要在类型名称后面的<>中声明一系列的类型变量s就行了。这些类型变量s就是返回值类型或者参数类型。根据惯例,一个类型变量用一个大写字母表示。

 

 
标签:   :)  


标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模 逻辑回归是一种常用于分类任务的线性模,特别适用于二类问题。在信用评估中,该模可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模的适应性。 六、模构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模的召回率与特异性。 七、集成学习方法 为提升模预测能力,可采用集成策略,如结合多个模的预测结果。这有助于降低单一模的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值