惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
O
OpenAI News
V
V2EX
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
小众软件
小众软件
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - Franky
Security Latest
Security Latest
S
SegmentFault 最新的问题
Project Zero
Project Zero
Spread Privacy
Spread Privacy
K
Kaspersky official blog
J
Java Code Geeks
V
Vulnerabilities – Threatpost
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
WordPress大学
WordPress大学
量子位
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
腾讯CDC
Cyberwarzone
Cyberwarzone
IT之家
IT之家
GbyAI
GbyAI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
云风的 BLOG
云风的 BLOG
T
Troy Hunt's Blog
D
Docker

博客园 - Star.Stroll

【Chrome扩展】Reload image 重载图片 [原创]RegularJS [原创]虚拟化技术真的能让企业降低成本吗? [原创]纯Java获得本地MAC地址 [原创]GCC动态链接库的编译与使用简例 [原创]Cygwin的中文支持(解决乱码) [原创]程序员日志:自制随笔程序 [原创]程序员日志:程序员的快捷方式 [原创]Java Socket(套接字)自学笔记1 [原创]透明的DIV提示框(兼容IE与Firefox) 深入HashCode方法 JavaScript代码实例:拖动对象 Drag Object (兼容:IE、Firefox、Opera ... ) Tomcat中用web.xml控制Web应用详解(2) Tomcat中用web.xml控制Web应用详解(1) java.sql.ResultSet 新增、更新、刪除資料 [原创]一个简单例子解释 Java 工厂模式 [原创]Object.clone()的作用与用法 Singleton模式(单态模式) Builder模式
Java 范型编程
Star.Stroll · 2008-01-26 · via 博客园 - Star.Stroll

Generics(范型)可以称之为参数类型(parameterized types),由编译器来验证从客户端将一种类型传送给某一对象的机制。如Java.util.ArrayList,编译器可以用Generics来保证类型安全。

1.一个简单的范型示例

    在以前,你可能遇到过这样的代码:

 List list = new LinkedList();

 list.add(“麻省理工”);

 list.add(“普林斯顿”);

 list.add(“伯克利”);

 String name 
= (String)list.iterator.next();

    注意,第三行需要强制转换。而使用范型:

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

 list.add(“麻省理工”);

 list.add(“普林斯顿”);

 list.add(“伯克利”);

 String name 
= list.iterator.next();

    这里将list声明成String类型的List。List是有一个类型参数的范型接口。这个例子中类型参数是String。

2.定义简单的范型

    看j2se5.0中List和Iterator接口的实现(片断):

 public interface List<E>

     
void add(E x);

     Iterator
<E> iterator();

 }


 
public interface Iterator<E> 

     E next();

     
boolean hasNext();

 }

    上面的代码我们比较熟悉,但是其中增加了尖括号。尖括号中的内容定义了接口List和Iterator的形式类型参数。类型参数可以用在范型声明中,如类和接口的声明。
    一旦声明了范型,你就可以使用它。在上面的例子中使用了List<String>。这里使用String是实参,代替了形参E。如果使用List<Integer>,则用实参Integer代替了形参E。
    不管List<Integer>还是List<String>,它们的类只有一个。考虑下面的代码:

 List<String> list1 = new LinkedList<String>();

 List
<Integer> list2 = new LinkedList<Integer>();

 System.out.println(list1.getClass()
==list2.getClass());

    输出结果为true。

    一般来说,形式类型参数都是大写,尽量使用单个字母,许多容器类都使用E作为参数。

3.范型和继承

    考虑下面的代码,你认为它会出错吗?

 String s = "smallnest@163.com";

 Object o 
= s;

    当然,String类继承Object类,这样做不会出错。但下面的代码呢?

 List<String> s = new LinkedList<String>();

 List
<Object>o=s;

    编译出错!是的,List<Object>和List<String>没有继承关系。
4.通配符

    考虑下面一个方法:

 public void printCollection(Collection<Object> c){

    
for(Object o:c){
        System.out.println(o);
    }


 }

    事实上,上面这个方法并不通用,它只能打印Collection<Object>类型的集合,象其他的如Collection<String>、Collection<Integer>并不能被打印,因为对象类型不一致。

    为了解决这个问题,可以使用通配符:

 public void printCollection(Collection<?> c){

    
for(Object o:c){
        System.out.println(o);
    }


 }

    Collection<?>被称作未知类型的集合。问号代表各种类型。

    上面的读取集合中的数据时,我们采用Object类型。这样做时可以的,因为不管未知类型最终代表何种类型,它的数据都继承Object类,那么再考虑一下下面的代码:

 Collection<?> c = new ArrayList<String>();

 c.add(
new Object());

    这样做时错误的,因为我们不知道?代表何种类型,所以我们不能直接将Object增加到集合中,这会出现类型不匹配的情况。

5.有限制的通配符

    考虑下面的代码:

class Man{
   
public String name = "";
}


class GoodMan extends Man{
   
public String name = "";
}


class BadMan extends Man{
   
public String name = "";
}

public void printName(List<Man> men){
   
for(Man man:men){
      System.out.println(
"姓名:" + man.name);
   }

}

    这个范型方法只能显示List<Man>类型的数据,下面的代码允许显示Man和它的子类。

public void printName(List<? extends  Man> men){
   
for(Man man:men){
      System.out.println(
"姓名:" + man.name);
   }

}

    这里使用? extends Man代替Man,表明接受任何Man的子类做为参数。
    和前面的代码类似,下面的代码也是不正确的:

public void adman(List<? extends Man> men){
    GoodMan good 
= new GoodMan();
    good.name 
= "晁岳攀";
    men.add(good);
}

    原因也很简单,因为?代表一切继承Man的类,你并不能保证就一定时GoodMan类。和这种用法类似:

public void adman(List<? super GoodMan> men){
    GoodMan good 
= new GoodMan();
    good.name 
= "晁岳攀";
    men.add(good);
}

6.范型方法

    考虑下面的代码,我们将一个数组的内容加到一个集合中

public  void copyArrayToCollection(Man[] men, Collection<?>c){
    
for(Man man:men){
       c.add(man);
    }

}

    这段代码时错的!因为我们并不知道集合C的类型,所以不能将Man类型的数据加到集合中。

    可以使用范型方法解决:

public <T> void copyArrayToCollection(T[] men, Collection<T>c){
    
for(T man:men){
       c.add(man);
    }

}

    这里T时一个形式类型参数。何时该采用通用方法?何时该采用通配符?

    考虑下面的例子:

interface  Collection<E>{
    
public boolean containsAll(Collection<?> c);
    
public boolean addAll(Collection<? extends E> c);
}

    改写成通用方法:

interface  Collection<E>{
    
public <T> boolean containsAll(Collection<T> c);
    
public <extends E> boolean addAll(Collection<T> c);
}

然而,在这里每个方法T只使用了一次,返回值不依赖形式参数,其他参数也不依赖形式参数。这说明实参被用作多态,这种情况下就应该用通配符。