专利名称:实现参数化类型与现有非参数化库兼容的方法
技术领域:
本发明涉及计算机软件,更具体地讲,涉及类型(type)的参数化。
最近,面向对象编程已经成为标准编程范例。在面向对象编程中,将世界以对象模型化。对象(object)是一个组合了操纵它的过程(procedure)和函数(function)的记录。
对象的行为是由类(class)规定的,类类似于传统编程语言的“数据类型”,但是如同JavaTM编程语言这样的语言中说明的那样,额外地用来通过一种继承机制把对象分类成分层结构。类用作从中可以建立对象的模板。一个类中的所有对象具有相同的域(field)(“属性(attribute)”),并且由相同的过程和函数(“方法(method)”)操纵。一个对象被说成是它所属类的一个“示例(instance)”。
一个对象的各种属性的本身可以是各种对象,因而属于它们自己的类。在不同环境下,最好使各对象仅在它们的属性所属的类上彼此不同。例如,可能需要使一个特定的应用程序具有一个串向量(vectorof string),一个字符向量(vector of character),和一个串向量的向量(vector of vector of string)。在这些情况的每一个中,各向量的不同仅在于存储在向量中的元素的类型。
一种解决方案是给每一个属性类型定义一个独立的类定义。例如,可以给具有属于字符类(class of Characters)属性的向量定义一个向量类,给具有属于串类(class of Strings)属性的向量定义另一个向量类,而给具有属于串向量类(class of Vector of Strings)属性的向量定义另一个向量类。
下面的代码实例使用了JavaTM编程语言定义一个具有属于字符类的属性的特定类型的(type-specific)Vector类。
特定类型的类<pre listing-type="program-listing"><![CDATA[10 class CharacterVector{12 public static final int CAPACITY=4;14 private Character[]oa=new Character[CAPACITY];18 public Character get(int i){20 if(0<=i&&i<CAPACITY)return oa[i];22 else throw new IndexOutOfBoundsException();24 }26 public void set(int i,Character val){28 if(0<=i&&i<CAPACITY)oa[i]=val;30 else throw new IndexOutOfBoundsException();32 }34 }]]></pre>在这个实例中,类Vector包括两个属性(CAPACITY和oa),和两个方法(get和set)。oa属性在14行中被定义为一个“字符”型元素的数组。CAPACITY属性是一个定义数组oa的最小存储容量的整常数。在本例中,最初分配数组oa存储不少于四个元素(字符)。
调用在18-24行中定义的get(取)方法从oa向量中取出第i个元素。get方法取得一个整数型的输入参数“i”,并且返回一个字符型的值。在20行,如果索引值“i”在范围内,那么返回数组oa的位置“i”的字符。在26-32行中定义的set(设置)方法取得整数型的输入参数“i”,和字符型输入参数val。调用set方法把oa向量中的第i个元素设置为参数val定义的字符。在这两种方法中,如果索引值“i”不在范围内,那么放弃(throw)越界异常(IndexOutOfBoundsException)。
作为替代,可以用Java编程语言如下定义具有属于串类属性的特定类型的Vector类特定类型的类<pre listing-type="program-listing"><![CDATA[40 class StringVector{42 public static final int CAPACITY=4;44 private String[]oa=new String[CAPACITY];48 public String get(int i){50 if(0<=i&&i<CAPACITY)return oa[i];52 else throw new IndexOutOfBoundsException();54 }56 public void set(int i,String val){58 if(0<=i&&i<CAPACITY)oa[i]=val;60 else throw new IndexOutOfBoundsException();62 }64 }]]></pre>在这个实例中,类Vector包括两个属性(CAPACITY和oa),和两个方法(get和set)。在行44中把oa属性定义为一个“串”型元素的数组。CAPACITY属性是一个定义oa最小容量的整常数。在本例中,oa将保持至少四个元素(串)。
调用在行48-54中定义的get方法从oa向量中取出第i个元素。get方法取得一个整数型输入参数“i”,并返回一个串型值。在行50,如果索引值“i”在范围内,那么返回数组oa的位置“i”的串。行56-62中定义的set方法取得整数型的输入参数“i”,和串型的输入参数val。调用set方法把oa向量中的第i个元素设置为参数val定义的串。在这两种方法中,如果索引值“i”不在范围内,那么调用越界异常例程(IndexOutOfBoundsException)。
给每个属性类型(即,字符,串,等等)定义一个特定类型的类的缺点在于,必须把存储器分配给每个特定类型的类定义。把存储器的存储空间分配给每个特定类型的类会因为冗余码消耗存储器而造成浪费和低效。
减少把存储空间分配给每个特定类型的类定义造成的冗余的一种方法是具有一个通用的类定义。一个通用类定义将一个或多个属性规定为通用对象类的。由于所有类都是从通用对象类转变而来的,因而编程者能够自由地把特定类型的类作为通用类型对象类存储。例如,可以把一个Vector类定义为具有“对象(Object)”型属性。通过把通用Vector类定义为具有对象型属性,可以把一个单一的类定义用于处理多个属性类型(即,字符,串,等等)。
下面的编码实例利用JavaTM编程语言定义了一个具有属于对象类的属性的通用Vector类。
通用型向量类(非参数化的)<pre listing-type="program-listing"><![CDATA[70 class Vector{72 public static final int CAPACITY=4;74 private Object[]oa=new Objcet[CAPACITY];78 public Object get(int i){80 if(0<=i&&i<CAPACITY)return oa[i];82 else throw new IndexOutOfBoundsException();84 }86 public void set(int i,Object val){88 if(0<=i&&i<CAPACITY)oa[i]=val;90 else throw new IndexOutOfBoundsException();92 }]]></pre>
<pre listing-type="program-listing"><![CDATA[94 }]]></pre>在这个实例中,类Vector包括两个属性(CAPACITY和oa),和两个方法(get和set)。在行74把oa属性定义为一个“对象”型的元素的数组。CAPACITY属性是一个定义了要在数组oa中为其分配空间的元素(对象)的最小数量的整常数。
调用在78-84行中定义的get方法从oa向量中取出第i个元素。get方法取得一个整数型的输入参数“i”,并且返回一个对象型的值。在行80,如果索引值“i”在范围内,那么返回在数组oa的位置“i”的对象。在行86-92中定义的set方法取得整数型的输入参数“i”,和对象型的输入参数val。调用set方法把oa向量中的第i个元素设置为参数val定义的对象。在这两个方法中,如果索引值“i”不在范围内,那么放弃越界异常(IndexOutOfOBoundsException)。
由于Vector定义规定了通用类型(即,对象型)的元素,因而可以从这个单一的类定义例示具有不同属性类型(即,字符,串,等等)的向量。
例如,利用如前面70-94行中定义的Vector类定义,可以将下面用户程序代码行用于例示一个具有对象型属性的向量。
用户程序代码(非参数化的)100 Vector iv=new Vector();//产生一个对象的向量如行100中所示,当需要一个新向量时,通过请求一个新向量(iv)例示一个对象型向量(iv)。由于例示向量“iv”具有对象型元素,因而它可能包含任何是对象型子代的对象类的元素。由于对象型(typeObject)是一个所有其它对象类型从它转变而来的通用对象类,因而向量iv可以包含任何对象类的元素(即,字符,串,等等)。因此,一个具有对象型元素的单一向量类可以用于例示具有不同元素类型(即,字符,串,等等)元素的向量。因为可以把一个单一的类定义用于提供存储大量类型元素的向量对象,这使得内存分配减少。
与用这种方式定义通用向量类型有关的缺点在于,迫使程序员必须跟踪存储在每个向量中的元素的类型,并且在从一个特定向量提取一个元素时必须使用一个适当的显式“投影(cast)”。投影是一种用于把一个较不特殊的类型显式地转换成一种更特殊类型的机制(例如,把一个“对象”型变量转换成一个字符型变量)。例如,使用JavaTM编程语言,一个利用70-94行中定义的向量类的用户程序代码的片段可以如下非参数化用户程序源代码<pre listing-type="program-listing"><![CDATA[102 Vector cv=new Vector();104 cv.set(0,new Character('a'));106 cv.set(1,new Character('b'));108 Character c=(Character)cv.get(0);110 Vector sv1=new Vector();112 sv1.set(0,"zero");114 sv1 set(1,"one");116 String s=(String)sv1.get(0);118 Vector sv2=new Vector();120 sv2.set(0,"one");122 String s2=(String)sv2.get(0);124 Character i2=(Character)sv1.get(0);//运行时错误]]></pre>这个用户程序代码实例的执行提供了以下结果。在行102,110和118,具有对象型属性的向量被分别例示为向量iv,sv1和sv2。在行104,在向量cv的位置0的元素被设置等于字符“a”,并且在行106,在向量cv的位置1的元素被设置等于字符“b”。在行108,一个字符变量c被设置等于向量cv中位置0的元素。如上所述,必须使用一个适当的投影将一个较不特殊的类型显式地转换成一个更特殊类型(例如,从一个对象型变量转换成一个字符型变量)。
在112行,向量sv1中位置0的元素被设置等于串“zero”,在114行,向量sv1中位置1的元素被设置等于串“one”。在116行,一个串变量s被设置等于向量sv1中位置0的元素。在这里也必须使用一种适当的投影,把一个较不特殊的类型显式地转换成一个更特殊的类型(例如,从一个对象型的变量转换成一个串型的变量)。
在120行,向量sv2的位置0的元素被设置等于串“one”,并且在122行,利用适当的投影把串变量s2设置等于向量sv2中位置0的元素。
如124行中所示,与使用一个通用类型向量类相关的缺点在于,如果试图把一个向量元素投影成一个“不兼容”变量类型(即,试图把串型的向量元素(向量sv1的位置0元素)投影成一个字符型变量(c2)),发生运行时错误。
在某些语言中,例如JavaTM编程语言中,由于语言是静态类型安全的,需要程序员自己插入投影,因此限制了编译程序在编译时插入适当的投影。因此,使用如70-97行中所示的通用类型类,程序员要花费很大精力把每个对象型元素正确地投影成它的正确类型,以避免引入一般不能在编译时发现的不兼容类型赋值错误。此外,通过要求使用投影,由于执行类型投影指令需要使用额外的计算周期,因而引入了额外的运行时系统开销。
避免进行投影对程序员造成的负担的一种方法是通过使用参数化类。参数化类是一种其中类的元素类型由一个参数定义的类。提供一个类型参数表的类定义在这里称为参数化类。由于参数的类型是在使用参数化类的代码中规定的,可以在编译时在使用参数化类的代码的上下文中检测出不兼容赋值和类型投影。
例如,可以把一个替代上述向量类定义(70-94行)的参数化类定义如下参数化类库源代码<pre listing-type="program-listing"><![CDATA[130 class Vector<A>{132 public static final int CAPACITY=4;134 private A[]oa=new A[CAPACITY];138 public A get(int i){140 if(0<=i&&i<CAPACITY)return oa[i];142 else throw new IndexOutOfBoundsException();144 }146 public void set(int i,A val){148 if(0<=i&&i<CAPACITY)oa[i]=val;150 else throw new IndexOutOfBoundsException();152 }154 }]]></pre>在这个实例中,类Vector包括一个参数“A”,两个属性(CAPACITY和oa),和两个方法(get和set)。在134行把oa属性定义为一个“A”型的元素的数组。CAPACITY属性是一个定义分配在数组oa中存储的元素(对象)的数量的整常数。
调用在138-144行中定义的get方法从oa向量中取出第i个元素。get方法取得一个整数型的输入参数“i”,并且返回一个A型的值。在140行,如果索引值“i”在范围内,那么返回数组oa的位置“i”的元素。在146-152行中定义的set方法取得整数型的输入参数“i”,和A型的输入参数val。调用set方法将oa向量中的第i个元素设置到已经定义为A型的参数val。在这两个方法中,如果索引值“i”不在范围内,那么放弃越界异常(IndexOutOfBoundsException)。
通过参数化向量类定义,可以把一个单一类定义用于例示具有不同属性类型(即,字符,串,等等)的元素的向量。此外,通过使用一个参数化向量类定义,在编译时可以识别包含在每个向量示例中的元素的类型。这使得能够在编译期间进行静态类型检查和不兼容类型赋值的识别,而不需要显式的类型投影。
例如,利用JavaTM编程语言,链接到上述参数化向量类(130-154行)的一个用户程序代码片段的编译可以如下。
参数化用户程序源代码<pre listing-type="program-listing"><![CDATA[160 Vector<Character>cv=new Vector<Character>();//建立字符向量162 cv.set(0,1);164 cv.set(2,5);166 Character c=cv.get(0);//不需要投影168 Vector<String>sv1=new Vector<String>();//建立第一串向量170 sv1.set(0,"zero");172 sv1.set(1,"one");174 String s=sv1.get(0);//不需要投影176 Vector<String>sv2=new Vector<String>();//建立第二串向量178 sv2.set(0,"one");180 String s2=sv2.get(0);//不需要投影182 Character c2=sv1.get(0);//编译时错误]]></pre>这个参数化类代码实例的执行提供以下结果。在160行,例示了一个具有字符型的元素的向量(cv)。具体地讲,在160行中调用向量构造函数(“new”方法)时传递的“character”参数造成一个新向量的产生,其中的属性“oa’是一个字符数组。同样,在168和176行中,通过调用向量构造函数并且把“String”作为参数值传递,例示了具有串型元素的向量(sv1和sv2)。
在162行,将向量cv的位置0的元素设置等于字符“1”,并且在164行把向量cv的位置2的元素设置等于字符“5”。在166行,把一个字符变量c设置等于向量cv中位置0的元素。由于前面把向量cv例示为具有字符型的属性(160行),因而编译程序能够确定向量cv是否包含与字符变量c兼容的属性。因此,不再需要一个投影指令来把一个较不特殊的类型显式地转换成一个更特殊的类型。
在170行,将向量sv1的位置0的元素设置等于串“zero”,在172行,把向量sv1的位置1的元素设置等于串“one”。在174行,把向量sv1中位置0的元素赋予一个串变量s。在这里也是由于前面已经把向量sv1例示为具有串型的属性(168行),因而编译程序能够确定向量sv1是否包含与串变量s兼容的属性。此外,不再需要一个投影指令把一个较不特殊的类型显式地转换成一个更特殊的类型。
在178行,把向量sv2的位置0元素设置等于串“one”,在180行,把一个串变量s2设置等于向量sv2中位置0的元素(串),而不使用投影指令。
在182行,试图将串型的向量(sv1)的一个元素赋予一个字符型的变量(c2)。通过使每个向量例示包含一个参数(如130-154行中类定义需要的),编译程序不用显式的类型投影就可以确定每个向量示例的元素类型。在知道了与一个特定向量示例有关的元素的类型时,编译程序可以执行静态类型检查以确定一个变量类型是否与该特定向量示例包含的元素类型兼容。
例如,通过在160行在向量cv的例示中包括一个参数“Character”,编译程序知道从这一点向前向量cv的元素是字符型的。这使得编译程序能够执行有关所有涉及向量cv的元素的赋值的静态类型检查。因此,静态类型检查提供了能够在编译时,而不是运行时,识别不兼容类型赋值的好处。例如,通过182行说明了使用静态类型检查的好处,在182行中试图将一个以前(行168)定义(例示)为串型的矢量sv1的元素赋予一个字符型的变量“c2”。
由于可以在编译时查获不兼容类型赋值错误,因而参数化类的使用可以显著减小不兼容类型投影造成的运行时错误的数量。此外,由于可以在编译时确定对一个特定类示例赋予的元素类型,因而程序员不再需要写入在从一个特定类示例提取一个元素时进行投影的代码。
尽管参数化类类型提供允许静态类型检查的额外好处,但是在参数化类类型使用之前已经开发并安装了大量的库代码(即,利用非参数化库代码产生的)。因此,在开发非参数化库代码中已经投入了大量的财力和人力投资。在某些情况下,非参数化库代码已经分发到全世界并且在世界各地使用。例如,在参数化类类型使用之前已经用JavaTM编程语言开发了大量的类代码。这种非参数化类代码已经被编译成分发到世界各地并且正在世界上使用的类库字节码。
如上所述,非参数化类代码具有不允许编译程序执行静态类型检查和需要程序员把显式类型投影指令插入到非参数化用户程序源代码中的缺点。因此,如果在首次开发类库时参数化已经可用的话,那么大多数现有的非参数化类库就会更容易地连接和开发类代码。
在仍然利用参数化类的同时产生“新的”用户程序字节码的一种方法是利用异种转换把参数化代码转换成非参数化代码。异种转换是一种造成对每个参数化类使用的参数值产生一个不同类的转换法。异种转换法一般是在C++之类的语言中使用的,其中使用一种宏扩展形式为每个类类型参数产生一个新的类类型的示例。
例如,对前面130-154和160-182行中所示的参数化类代码执行异种转换,对不同类参数值(即,字符和串)将产生以下类。
<pre listing-type="program-listing"><![CDATA[200 class VectorCharacter{202 public static final int CAPACITY=4;204 private Character[]oa=new Character[CAPACITY];208 public Character get(int i){210 if(0<=i&&i<CAPACITY)return oa[i];212 else throw new IndexOutOfBoundsException();214 }216 public void set(int i,Character val){218 if(0<=i&&i<CAPACITY)oa[i]=val;220 else throw new IndexOutOfBoundsException();222 }224 }230 class VectorString{232 public static final int CAPACITY=4;234 private String[]oa=new String[CAPACITY];238 public String get(int i){240 if(0<=i&&i<CAPACITY)return oa[i];242 else throw new IndexOutOfBoundsException();244 }246 public void set(int i,String val){248 if(0<=i&&i<CAPACITY)oa[i]=val;250 else throw new IndexOutOfBoundsException();252 }254 }]]></pre>
图1示出了一个其中在利用参数化类代码的同时使用异种转换产生“新的”类文件的方框图。图1是根据前面的Vector类代码实例说明的。如图1中所示,类文件102是由非参数化用户程序字节码106和非参数化类库字节码104组成的。通过编译110非参数化类库源代码116(代码行70-94)生成非参数化类库字节码104,以产生一个对象的向量(Vector of Object)。非参数化用户程序字节码106是通过用非参数化类定义114编译108非参数化用户程序源代码112(代码行102-124)产生的,并用于调用对象的向量中的方法。非参数化类定义114提供了用于连接非参数化用户程序源代码112和非参数化类库字节码104的编译时接口定义。如双箭头130所示,非参数化用户程序字节码106与非参数化类库字节码104是兼容的,因而可以与之连接。
参数化用户程序源代码126代表用参数(字符和串)开发的程序代码(代码行160-182)。参数化类定义128提供了连接参数化用户程序源代码126与非参数化类库字节码104的接口定义。在产生用户程序字节码118中,执行对参数化用户程序源代码126和参数化类定义128的静态类型检查124。如上所述,执行对参数化源代码的静态类型检查以确定代码是否包含任何不兼容类型赋值。在执行了静态检查124之后,执行异种转换122,以便为参数化用户程序源代码126中使用的每个类类型参数产生一个类示例(即,200-224和230-254行)。因此,参考前面的代码实例,所有具有“对象”型的元素的向量示例被转换成具有该特定类类型参数的元素的向量示例。例如,利用异种转换法,在上述160行中的代码将产生一个字符型的向量的示例(200-224行),并且在168和176行的代码将产生一个串型的向量的示例(230-254行)。然后把来自异种转换122的输出编译120成用户程序字节码118,其调用字符向量和串向量的方法。
有关使用异种转换法产生用户程序字节码的缺点在于,从参数化类型产生的字节码与最初用非参数化类库产生的字节码具有不同形式。具体地讲,利用异种转换法,新产生的字节码包含对用每个参数类型(即,字符,串)规定的每个元素类型的类的额外引用。但是,从非参数化类文件编译的字节码不包含这些额外的类引用。这些额外引用造成从参数化类文件产生的字节码与在当前使用的现有系统上从非参数化类文件产生的字节码不兼容。这些执行字节码的系统一般称为“代码执行程序(code executor)”。
例如,在开发诸如Java虚拟机(JVM)之类的代码执行程序中投入了大量资金。JVM翻译字节码以产生作为由类定义定义的类的示例的对象。这些对象控制JVM的执行。在Tim Lindholm和Frank Yellin著作的“Java虚拟机说明(The Java Virtual Machine Specification)(1996)”中详细地描述了JVM。由于JVM期望字节码包含一个特定类型(例如,对象)的类,因而利用异种转换法产生的字节码是不兼容的,并且不能由JVM正确地翻译。因此,这些JVM不能利用使用异种转换法参数化非参数化类库的优点。
根据以上说明,希望开发一种能够提供利用参数化代码的好处(例如,在编译时捕获错误,避免对显式类型投影的需要),但仍然允许使用老的非参数化类库和不支持参数化代码的老的虚拟机的机制。
本发明提供了一种利用参数化类产生与以前利用非参数化类产生的现有类库兼容的代码的方法和装置。
根据这种方法,接收参数化源代码,该源代码包含属于通过把参数值提供到一个参数化类定义而定义的多种类型的变量。执行对参数化源代码的静态类型检查,以确定属于多种类型的变量与赋予变量的值之间是否存在任何不兼容类型赋值。如果不存在不兼容类型赋值,那么对参数化源代码执行同种转换,以产生非参数化类代码。然后编译非参数化类代码以产生与利用非参数化类产生的现有类库兼容的代码。
根据本发明的另一方面,在执行对参数化源代码的同种转换中,确定一个所有参数值都可以是它的成员的擦除类型。然后,用擦除类型替代参数值。
根据本发明的再一方面,把一个或多个标记加到代码上,用于识别代码是用参数化类产生的。
本发明是在附图中以举例的方式而不是限制的方式说明的,附图中相同的参考号代表相同的单元,其中图1是现有技术中使用的利用异种转换法实现参数化类型的方框图;图2是一个其上可以实现本发明的一个实施例的计算机系统的方框图;图3是利用一种同种转换法实现参数化类型的方框图;和图4是说明利用同种转换法实现参数化类型的方法的流程图。
以下说明用于实现参数化类类型与现有非参数化库兼容的方法和装置。在下面的说明中,为了解释的目的,提出了许多特定的细节,以便提供对本发明的全面理解。但是,熟悉本领域的人员应当知道,可以不用这些特定细节实现本发明。在其它示例中,用方框图的形式显示了众所周知的结构和设备,以便避免不必要地混淆本发明。硬件概述图2是说明其上可以实现本发明的一个实施例的计算机系统200的方框图。计算机系统200包括一个用于交流信息的总线202或其它通信机构,和一个与总线202耦合的用于处理信息的处理器204。计算机系统200还包括一个耦合到总线202用于存储信息和处理器204执行的指令的随机存取存储器(RAM)或其它动态存储器件之类的主存储器206。主存储器206也用于在执行处理器204执行的指令过程中存储临时变量或其它中间信息。计算机系统200进一步包括耦合到总线202的用于存储静态信息和处理器204指令的只读存储器(ROM)208或其它静态存储器件。提供有磁盘或光盘之类的存储设备210,并耦合到总线202,用于存储信息和指令。
可以经过总线202把计算机系统200耦合到一个阴极射线管(CRT)之类的显示器212,用于把信息显示给计算机用户。将包括字母键和其它键的输入设备214耦合到总线202,用于把信息和命令选择提供到处理器204。另一种类型的用户输入设备是鼠标器、轨迹球或光标方向键之类的光标控制器216,用于将方向信息和命令选择提供到处理器204和用于控制光标在显示器212上的移动。这种输入设备一般具有在一个第一轴(例如,x轴)和一个第二轴(例如,y轴)的两个轴上的自由度,这使得设备能够指出一个平面上的位置。
本发明涉及使用计算机系统200实现参数化类类型与现有非参数化库的兼容。根据本发明的一个实施例,通过计算机系统200响应执行一个或多个存储在主存储器206中的指令的一个或多个序列的处理器204提供实现参数化类类型与现有未参数化库的兼容。这种指令可以从诸如存储设备210之类的另一个计算机可读介质读出到主存储器206。包含在主存储器206中的指令序列的执行使处理器204进行这里所述的处理步骤。在替代实施例中,可以使用硬布线电路替代或组合软件指令以实现本发明。因此,本发明的实施例不限于硬布线电路和软件的任何特定组合。
这里使用的术语“计算机可读介质”是指任何参与向处理器204提供执行的指令的介质。这种介质可以有多种形式,包括但不限于,非易失性介质,易失性介质,和传输介质。非易失性介质包括,例如,诸如存储设备210之类的光盘或磁盘。易失性介质包括像主存储器206这样的动态存储器。传输介质包括同轴电缆,铜线和光纤,包括构成总线202的布线。传输介质也可以采用声或光波的形式,例如在无线电波和红外线数据通信过程中产生的声或光波。
计算机可读介质的一般形式包括,例如,软盘,柔性磁盘,硬盘,磁带,或任何其它磁性介质,CD-ROM,任何其它光学介质,穿孔卡,穿孔纸带,或任何带有孔图案的物理介质,RAM,PROM,EPROM,FLASH-EPROM,任何其它存储芯片或盒式磁盘,后面将说明的载波,或任何其它计算机可从中读取的介质。
可以用各种形式的计算机可读介质携带处理器204执行的一个或多个指令的一个或多个序列。例如,指令最初可以携带在一个远端计算机的磁盘上。远端计算机可以把指令装载到它的动态存储器中,并利用调制解调器通过电话线路传送指令。位于计算机系统200的调制解调器可以接收电话线路上的数据,并利用红外发射机把数据转换成红外信号。一个耦合到总线202的红外检测器可以接收红外信号中携带的数据,并把数据传送到总线202。总线202将数据传送到主存储器206,处理器204从主存储器206取出和执行指令。可以有选择地把主存储器206接收的指令在处理器204执行之前或之后存储在存储设备210中。
计算机系统200还包括一个耦合到总线202的通信接口218。通信接口218提供了对一个连接于一个本地网222的网络链路220的双向数据通信耦合。例如,通信接口218可以是一个综合业务数字网(ISDN)卡或一个调制解调器,以提供到对应类型的电话线路的数据通信连接。作为另一个例子,通信接口218可以是一个局域网(LAN)卡,以提供对一个兼容LAN的数据通信连接。也可以实现无线链路。在任何这种实现中,通信接口218发送和接收携带代表各种类型的信息的数字数据流的电、电磁或光信号。
网络链路220一般通过一个或多个网络提供对其它数据设备的数据通信。例如,网络链路220可以通过本地网222提供对一个主计算机224或对因特网业务提供商(ISP)226操作的数据设施的连接。ISP226又通过现在通称为“因特网”228的全球分组数据通信网提供数据通信服务。本地网222和因特网228都使用携带数字数据流的电、电磁或光信号。通过各种网络、以及在网络链路220上并且通过通信接口218向和从计算机系统200携带数字数据的信号都是传送信息的载波的示例形式。
计算机系统200可以通过网络、网络链路220和通信接口218发送消息和接收包括程序代码在内的数据。在因特网的例子中,服务器230可以通过因特网228,ISP 226,本地网222和通信接口218发送对一个应用程序的请求代码。根据本发明,如这里所述的那样,一种这样的下载应用程序保证了参数化类类型与现有非参数化库兼容的实现。
在处理器204接收到代码时它可以执行接收的代码,和/或存储在存储设备210或其它非易失性存储器中,以便以后执行。以这种方式,计算机系统200可以获得载波形式的应用程序代码。同种转换在本发明的一个实施例中,使用同种转换技术从参数化类文件产生字节码。这样产生的代码对于代码执行程序来说是与用非参数化类库产生的字节码等效的。同种转换与异种转换的不同之处在于,是将一个单一的参数化类转换成一个单一的通用类,而不是转换成多个特定类型类。例如,可以用对象通用类型代替字符和串类型,因为字符类型和串类型都来自于单一的对象类型。
代替每个类类型的通用类型成为“擦除”。擦除代表一个所有类都可以是它的成员的通用类型。例如,可以用对象向量(Vector ofObject)类代表字符向量(Vector of Character)和串向量(Vector ofString)类。在一个实施例中,擦除是对象型的,并且执行同种转换造成每个参数化类类型被一个对象型类代替。
例如,利用向量的参数化类定义,一些向量可能包含字符型元素,而其它向量可能包含串型元素。如果擦除是对象型的,那么执行同种转换造成产生一个单一向量类示例,其中向量元素是对象型的。这个单一通用向量类提供了一个所有可能的类类型(即,本例中的字符和串)都可以是它的成员的类类型。通过对前面130-154和160-182中所示参数化类代码执行同种转换技术,将从不同类参数类型(即,字符和串)产生以下通用类。
<pre listing-type="program-listing"><![CDATA[260 class Vector{262 public static final int CAPACITY=4;264 private Object[]oa=new Object[CAPACITY];268 public Object get(int i){270 if(0<=i&&i<CAPACITY)return oa[i];272 else throw new IndexOutOfBoundsException();274 }276 public void set(int i,Object val){278 if(0<=i&&i<CAPACITY)oa[i]=val;280 else throw new IndexOutOfBoundsException();282 }284 }290 Vector cv=new Vector();292 cv.set(0,new Character('a'));294 cv.set(1,new Character('b'));296 Character c=(Character)cv.get(0);298 Vector sv1=new Vector();300 sv1.set(0,"zero");302 sv1.set(1,"one");304 String s=(String)sv1.get(0);]]></pre>
<pre listing-type="program-listing"><![CDATA[306 Vector sv2=new Vector();308 sv2.set(0,"one");310 String s2=(String)sv2.get(0);312 Character c2=(Character)sv1.get(0);]]></pre>图3示出了一个方框图,其中同种转换技术使用了参数化类产生与从非参数化类库产生的字节码等效的“新的”用户程序字节码。图3与图1类似,因此相同的组成部分标有相同的参考号。
图3是根据前面的Vector类代码实例说明的。在这个实例中,同种转换是参考前面说明的代码解释的。如图3中所示,非参数化用户程序字节码106和非参数化类库字节码104组成了类文件102。非参数化类库字节码104是通过编译110非参数化类库源代码116(代码行70-94)产生的,以生成一个对象向量(Vector of Object)。非参数化用户程序字节码106是通过用非参数化类定义114编译108非参数化用户程序源代码112(代码行102-124)而产生的,并且用于调用对象向量中的方法。非参数化类定义114提供了用于连接非参数化用户程序源代码112与非参数化类库字节码104的编译时接口定义。如双箭头130所示,非参数化用户程序字节码106与非参数化类库字节码104兼容,因而能够与之连接。
参数化用户程序源代码126代表用参数(Character和String)开发的程序代码(代码行160-182)。在产生“新的”用户程序字节码304时,进行有关参数化用户程序源代码126和参数化类定义128的静态类型检查124(代码行130-154)。如上所述,静态类型检查使得能够在“编译时”而不是运行时识别不兼容类型赋值。在执行了静态类型检查124之后,进行同种转换302以产生所有可能的类类型都可以是其成员的一个通用类类型(即代码行260-284)。因此,参考代码实例(160-182),具有“字符”型和“串”型元素的所有向量示例被转换成一个具有“对象”型元素的向量示例。例如,在一个实施例中,160,168和176行的代码将转换成“对象”型的向量的一个示例(70-94行)。然后,将来自同种转换302的输出编译120成调用对象向量(Vectorof Object)的方法的用户程序字节码304。如双箭头306所示,“新的”用户程序字节码304与非参数化类库字节码104兼容,因而能够与之连接。
图4示出了使用用于实现参数化类型的同种转换技术以产生与从非参数化类库产生的字节码等效的字节码的流程图。在步骤402,不用投影来写入参数化用户程序源代码126(160-182行)。在步骤404,执行对参数化用户程序源代码126和参数化类定义128的静态类型检查(130-154行),以确定代码是否包含任何不兼容类型赋值。在步骤406,在进行静态类型检查的同时,确定是否检测到任何不兼容类型赋值。如果检测到不兼容类型赋值,那么在步骤408,把不兼容类型赋值记录为编译时错误。然后控制流前进到步骤416。
否则,如果在步骤406没有检测到不兼容类型赋值,那么在步骤410,执行同种转换方法,把参数化用户程序源代码和类定义转换回到一个非参数化类文件。为了将参数化类文件代码(130-154和160-182)转换返回到非参数化类文件(260-284和290-312行),将每个类类型用其擦除代替。例如,参考130-154和160-182行,如果擦除类型是“对象”,那么通过删除该参数获得参数类型的擦除(即,把“Vector<A>”擦除到“Vector”,“Vector<Character>”擦除到“Vector”,“Vector<String>”擦除到“Vector”);通过用非参数类型自身的代替获得非参数类型的擦除(即,“Character”擦除到“Character”,和“String”擦除到“String”);和通过用擦除类型代替类型参数获得类型参数的擦除(即,“A”擦除到“Object”)。
在步骤412,在每个需要调用的方法之处插入投影,例如每当返回类型是一个类型参数时(即,用“String s=(String)sv.get(0)”代替“String S=sv.get(0)”,和用“Character c=(Character)cv.get(0)”代替“Character c=cv.get(0)”)。这产生了260-284和290-312行中所示的类文件代码。
在步骤414,把来自同种转换的输出(260-284和290-312行)编译成与利用非参数化用户程序源代码112和非参数化类定义114产生的“老的”用户程序字节码160等效的“新的”用户程序字节码304。在步骤416,将转换和编译结果报告给用户。在老虚拟机上执行字节码由于来自同种转换的输出被编译成与从非参数化库产生的字节码等效的字节码,老虚拟机不用参数化类文件就可以执行经过编译的但没有用参数化类文件发布的字节码。此外,由于对参数化类文件进行了静态类型检查,保证用户在老VM上执行编译的字节码时不会发生“不兼容”类型运行时错误。因此,在仍然产生与老代码执行程序兼容的字节码的同时,可以获得静态类型检查的好处。在改进的虚拟机上执行字节码通过对字节码增加某种老VM可以忽略的注解或标记,改进的VM可以获得通过忽略投影指令执行对参数化类文件的静态类型检查的利益。如上所述,通过使用参数化类文件,可以进行静态类型检查,以在编译时识别不兼容类型赋值。由于静态类型检查保证了不会发生不兼容运行时错误,因而在从一个类示例提取对象型元素时,改进的虚拟机不需要进行投影。因此,在本发明的某些实施例中,将指示该字节码是用参数化类文件产生的标记添加到字节码上。使用这些注解减小与执行投影相关的系统开销,因此,使改进的VM能够更有效地执行。总结如上所述,可以使用同种转换技术从参数化源代码产生对于代码执行程序来说与用非参数化类库产生的字节码等效的字节码。利用同种转换法,可以把参数化类型用于静态类型检查来自非参数化库的类文件,同时仍然产生与从非参数化库产生的字节码等效的字节码。因此,老虚拟机可以继续执行用参数化类文件编译的字节码。
此外,通过添加某些标记,由于保证用户在执行编译的字节码过程中不会发生“不兼容”类型运行时错误,改进的VM可以进一步从忽略投影指令的静态类型检查中获得利益。
尽管某些实施例是利用JavaTM编程语言说明的,但本发明可以在各种不同语言上实现,因而不应当认为本发明仅限于JavaTM编程语言。
在上述说明书中,本发明是参考其特定实施例说明的。但是应当知道可以对其进行各种改进和改变而不脱离本发明的广义的精神和范围。因此,应当把说明书和附图看成是说明性的,而不是限制性的。
权利要求
1.一种使用参数化类产生代码,从而使代码与用非参数化类产生的现有类库兼容的方法,该方法包括步骤接收参数化源代码,该源代码包含属于通过把参数值提供给一个参数化类定义而定义的多种类型的变量;对所述参数化源代码进行静态类型检查,以确定属于所述多种类型的变量和赋予所述变量的值之间是否存在不兼容类型赋值;如果不存在不兼容类型赋值,那么执行对参数化源代码的同种转换以产生非参数化类代码;和编译非参数化类代码以产生与现有类库兼容的代码。
2.根据权利要求1所述的方法,其中执行对参数化源代码同种转换的步骤包括步骤为参数值确定一个擦除类型,其中擦除类型是一个所有参数值都可以是它的成员的通用类型;和用擦除类型替代参数值。
3.根据权利要求2所述的方法,其中为参数化类型确定擦除类型的步骤包括把擦除类型设置为对象型的步骤。
4.根据权利要求1所述的方法,进一步包括给代码增加一个或多个标记的步骤,其中一个或多个标记可以用于识别代码是用参数化类产生的。
5.根据权利要求4所述的方法,进一步包括步骤在代码执行程序上执行代码,其中代码执行程序使用一个或多个标记确定代码是利用参数化类产生的;和代码执行程序一旦确定代码是用参数化类产生的,就忽略包含在代码中的投影指令,其中投影指令是同种转换的结果。
6.根据权利要求2所述的方法,进一步包括在代码中增加投影指令的步骤;其中投影指令用于把较不特殊的类型显式地转换成更特殊类型。
7.一种携带用于利用参数化类产生代码从而使代码与利用非参数化类产生的现有类库兼容的一个或多个指令序列的计算机可读介质,其中通过一个或多个处理器执行一个或多个指令序列使得一个或多个处理器执行以下步骤接收参数化源代码,该源代码包含属于通过把参数值提供给一个参数化类定义而定义的多种类型的变量;对所述参数化源代码进行静态类型检查,以确定属于所述多种类型的变量和赋予所述变量的值之间是否存在不兼容类型赋值;如果不存在不兼容类型赋值,那么执行对参数化源代码的同种转换以产生非参数化类代码;和编译非参数化类代码以产生与现有类库兼容的代码。
8.根据权利要求7所述的计算机可读介质,其中执行对参数化源代码同种转换的步骤包括步骤为参数值确定一个擦除类型,其中擦除类型是一个所有参数值都可以是它的成员的通用类型;和用擦除类型替代参数值。
9.根据权利要求8所述的计算机可读介质,其中为参数化类型确定擦除类型的步骤包括把擦除类型设置为对象型的步骤。
10.根据权利要求7所述的计算机可读介质,进一步包括用于执行把一个或多个标记加入到代码的步骤的指令,其中一个或多个标记可以用于识别代码是用参数化类产生的。
11.根据权利要求10所述的计算机可读介质,进一步包括用于执行以下步骤的指令在代码执行程序上执行代码,其中代码执行程序使用一个或多个标记确定代码是利用参数化类产生的;和代码执行程序一旦确定代码是用参数化类产生的,就忽略包含在代码中的投影指令,其中投影指令是同种转换的结果。
12.根据权利要求8所述的计算机可读介质,进一步包括用于执行把投影指令加入到代码的步骤的指令;其中投影指令用于把较不特殊的类型显式地转换成更特殊类型。
13.一种用于利用参数化类产生代码从而使代码与利用非参数化类产生的现有类库兼容的系统,该系统包括存储器;一个或多个耦合到存储器的处理器;和包含在存储器中的计算机指令集,计算机指令集包括在由一个或多个处理器执行时造成一个或多个处理器执行以下步骤的计算机指令接收参数化源代码,该源代码包含属于通过把参数值提供给一个参数化类定义而定义的多种类型的变量;对所述参数化源代码进行静态类型检查,以确定属于所述多种类型的变量和赋予所述变量的值之间是否存在不兼容类型赋值;如果不存在不兼容类型赋值,那么执行对参数化源代码的同种转换以产生非参数化类代码;和编译非参数化类代码以产生与现有类库兼容的代码。
14.根据权利要求13所述的系统,其中执行对参数化源代码同种转换的步骤包括步骤为参数值确定一个擦除类型,其中擦除类型是一个所有参数值都可以是它的成员的通用类型;和用擦除类型替代参数值。
15.根据权利要求14所述的系统,其中为参数化类型确定擦除类型的步骤包括把擦除类型设置为对象型的步骤。
16.根据权利要求13所述的系统,进一步包括给代码增加一个或多个标记的步骤,其中一个或多个标记可以用于识别代码是用参数化类产生的。
17.根据权利要求16所述的系统,进一步包括步骤在代码执行程序上执行代码,其中代码执行程序使用一个或多个标记确定代码是利用参数化类产生的;和代码执行程序一旦确定代码是用参数化类产生的,就忽略包含在代码中的投影指令,其中投影指令是同种转换的结果。
18.根据权利要求14所述的系统,进一步包括在代码中增加投影指令的步骤;其中投影指令用于把较不特殊的类型显式地转换成更特殊类型。
全文摘要
本发明公开了一种用于利用参数化类产生与以前利用非参数化类产生的现有类库兼容的代码的方法和装置。根据所述方法,接收参数化源代码,源代码包含属于多种类型的变量,多种类型是通过把参数值提供给一个参数化类定义而定义的。对参数化源代码执行静态类型检查,以确定属于多种类型的变量和赋予变量的值之间是否存在任何不兼容类型赋值。如果不存在不兼容类型赋值,那么对参数化源代码执行同种转换,以产生非参数化类代码。然后,编译非参数化类代码,以产生与利用非参数化类产生的现有类库兼容的代码。
文档编号G06F9/45GK1305609SQ99807469
公开日2001年7月25日 申请日期1999年6月16日 优先权日1998年6月16日
发明者戴维·P·斯托塔米雷 申请人:太阳微系统公司