java荟萃的缺点:范例未知
当前位置:以往代写 > JAVA 教程 >java荟萃的缺点:范例未知
2019-06-14

java荟萃的缺点:范例未知

java荟萃的缺点:范例未知

利用Java荟萃的“缺点”是在将工具置入一个集适时丢失了范例信息。之所以会产生这种环境,是由于当初编写集适时,谁人荟萃的措施员基础不知道用户到底想把什么范例置入荟萃。若指示某个荟萃只答允特定的范例,会故障它成为一个“通例用途”的东西,为用户带来贫苦。为办理这个问题,荟萃实际容纳的是范例为Object的一些工具的句柄。这种范例虽然代表Java中的所有工具,因为它是所有类的根。虽然,也要留意这并不包罗根基数据范例,因为它们并不是从“任何对象”担任来的。这是一个很好的方案,只是不合用下述场所:
(1) 将一个工具句柄置入集适时,由于范例信息会被丢弃,所以任何范例的工具都可进入我们的荟萃——即便出格指示它只能容纳特定范例的工具。举个例子来说,固然指示它只能容纳猫,但事实上任何人都可以把一条狗扔进来。
(2) 由于范例信息不复存在,所以荟萃能必定的独一工作就是本身容纳的是指向一个工具的句柄。正式利用它之前,必需对其举办造型,使其具有正确的范例。

值得欣慰的是,Java不答允人们滥用置入荟萃的工具。如果将一条狗扔进一个猫的荟萃,那么仍会将荟萃内的所有对象都看作猫,所以在利用那条狗时会获得一个“违例”错误。在同样的意义上,假使试图将一条狗的句柄“造型”到一只猫,那么运行期间仍会获得一个“违例”错误。
下面是个例子:
 

//: CatsAndDogs.java
// Simple collection example (Vector)
import java.util.*;

class Cat {
  private int catNumber;
  Cat(int i) {
    catNumber = i;
  }
  void print() {
    System.out.println("Cat #" + catNumber);
  }
}

class Dog {
  private int dogNumber;
  Dog(int i) {
    dogNumber = i;
  }
  void print() {
    System.out.println("Dog #" + dogNumber);
  }
}

public class CatsAndDogs {
  public static void main(String[] args) {
    Vector cats = new Vector();
    for(int i = 0; i < 7; i++)
      cats.addElement(new Cat(i));
    // Not a problem to add a dog to cats:
    cats.addElement(new Dog(7));
    for(int i = 0; i < cats.size(); i++)
      ((Cat)cats.elementAt(i)).print();
    // Dog is detected only at run-time
  }
} ///:~

可以看出,Vector的利用长短常简朴的:先建设一个,再用addElement()置入工具,今后用elementAt()取得那些工具(留意Vector有一个size()要领,可使我们知道已添加了几多个元素,以便防备误超界线,造成违例错误)。
Cat和Dog类都很是浅显——除了都是“工具”之外,它们并无出格之处(倘若不明晰指出从什么类担任,就默认为从Object担任。所以我们不只能用Vector要领将Cat工具置入这个荟萃,也能添加Dog工具,同时不会在编译期和运行期获得任何堕落提示。用Vector要领elementAt()获取原本认为是Cat的工具时,实际得到的是指向一个Object的句柄,必需将谁人工具造型为Cat。随后,需要将整个表达式用括号关闭起来,在为Cat挪用print()要领之前举办强制造型;不然就会呈现一个语法错误。在运行期间,假如试图将Dog工具造型为Cat,就会获得一个违例。
这些处理惩罚的意义都很是深远。尽量显得有些贫苦,但却得到了安详上的担保。我们以后再难偶尔造成一些埋没得深的错误。若措施的一个部门(或几个部门)将工具插入一个荟萃,但我们只是通过一次违例在措施的某个部门发明一个错误的工具置入了荟萃,就必需找出插入错误的位置。虽然,可通过查抄代码到达这个目标,但这或者是最笨的调试东西。另一方面,我们可从一些尺度化的荟萃类开始本身的编程。尽量它们在成果上存在一些不敷,且显得有些鸠拙,但却能担保没有埋没的错误。

1. 错误有时并不显暴露来
在某些环境下,措施好像正确地事情,不造型回我们本来的范例。第一种环境是相当非凡的:String类从编译器得到了特另外辅佐,使其可以或许正常事情。只要编译器等候的是一个String工具,但它没有获得一个,就会自动挪用在Object里界说、而且可以或许由任何Java类包围的toString()要领。这个要领能生成满意要求的String工具,然后在我们需要的时候利用。
因此,为了让本身类的工具能显示出来,要做的全部工作就是包围toString()要领,如下例所示:
 

//: WorksAnyway.java
// In special cases, things just seem
// to work correctly.
import java.util.*;

class Mouse {
  private int mouseNumber;
  Mouse(int i) {
    mouseNumber = i;
  }
  // Magic method:
  public String toString() {
    return "This is Mouse #" + mouseNumber;
  }
  void print(String msg) {
    if(msg != null) System.out.println(msg);
    System.out.println(
      "Mouse number " + mouseNumber);
  }
}

class MouseTrap {
  static void caughtYa(Object m) {
    Mouse mouse = (Mouse)m; // Cast from Object
    mouse.print("Caught one!");
  }
}

public class WorksAnyway {
  public static void main(String[] args) {
    Vector mice = new Vector();
    for(int i = 0; i < 3; i++)
      mice.addElement(new Mouse(i));
    for(int i = 0; i < mice.size(); i++) {
      // No cast necessary, automatic call
      // to Object.toString():
      System.out.println(
        "Free mouse: " + mice.elementAt(i));
      MouseTrap.caughtYa(mice.elementAt(i));
    }
  }
} ///:~

#p#分页标题#e#

可在Mouse里看到对toString()的重界说代码。在main()的第二个for轮回中,可发明下述语句:

System.out.println("Free mouse: " +
mice.elementAt(i));

在“+”后,编译器预期看到的是一个String工具。elementAt()生成了一个Object,所觉得得到但愿的String,编译器会默认挪用toString()。但不幸的是,只有针对String才气获得象这样的功效;其他任何范例都不会举办这样的转换。
埋没造型的第二种要领已在Mousetrap里获得了应用。caughtYa()要领吸收的不是一个Mouse,而是一个Object。随后再将其造型为一个Mouse。虽然,这样做长短常莽撞的,因为通过吸收一个Object,任何对象都可以通报给要领。然而,假使造型不正确——假如我们通报了错误的范例——就会在运行期间获得一个违例错误。这虽然没有在编译期举办查抄好,但仍然能防备问题的产生。留意在利用这个要领时毋需举办造型:
MouseTrap.caughtYa(mice.elementAt(i));

2. 生成能自动鉴别范例的Vector
各人或者不想放弃适才谁人问题。一个更“结实”的方案是用Vector建设一个新类,使其只吸收我们指定的范例,也只生成我们但愿的范例。如下所示:
 

//: GopherVector.java
// A type-conscious Vector
import java.util.*;

class Gopher {
  private int gopherNumber;
  Gopher(int i) {
    gopherNumber = i;
  }
  void print(String msg) {
    if(msg != null) System.out.println(msg);
    System.out.println(
      "Gopher number " + gopherNumber);
  }
}

class GopherTrap {
  static void caughtYa(Gopher g) {
    g.print("Caught one!");
  }
}

class GopherVector {
  private Vector v = new Vector();
  public void addElement(Gopher m) {
    v.addElement(m);
  }
  public Gopher elementAt(int index) {
    return (Gopher)v.elementAt(index);
  }
  public int size() { return v.size(); }
  public static void main(String[] args) {
    GopherVector gophers = new GopherVector();
    for(int i = 0; i < 3; i++)
      gophers.addElement(new Gopher(i));
    for(int i = 0; i < gophers.size(); i++)
      GopherTrap.caughtYa(gophers.elementAt(i));
  }
} ///:~

这前一个例子雷同,只是新的GopherVector类有一个范例为Vector的private成员(从Vector担任有些贫苦,来由稍后便知),并且要领也和Vector雷同。然而,它不会吸收和发生普通Object,只对Gopher工具感乐趣。
由于GopherVector只吸收一个Gopher(地鼠),所以如果我们利用:
gophers.addElement(new Pigeon());
就会在编译期间得到一条堕落动静。回收这种方法,尽量从编码的角度看显得更令人沉闷,但可以当即判定出是否利用了正确的范例。
留意在利用elementAt()时不必举办造型——它必定是一个Gopher。

3. 参数化范例
这类问题并不是孤独的——我们很多时候都要在其他范例的基本上建设新范例。此时,在编译期间拥有特定的范例信息长短常有辅佐的。这即是“参数化范例”的观念。在C++中,它由语言通过“模板”得到了直接支持。至少,Java保存了要害字generic,期望有一天可以或许支持参数化范例。但我们此刻无法确定这一天何时会光降。

    关键字:

在线提交作业