首页 > 编程知识 正文

JAVA通配符,java泛型上届

时间:2023-05-06 18:39:11 阅读:115606 作者:3137

类型安全虽然有用,但会影响代码结构,可能不完全可接受。 例如,假设在上一节的Stats类中添加一个方法sameAvg ),该方法确定两个Stats对象中数组的平均值是否相同,而不管每个对象包含的数字数据类型如何。 例如,如果一个对象包含双精度值1.0、2.0和3.0,而另一个对象包含整数值2、1和3,则平均值相同。 sameAvg ) )方法的一种实现方法是传递Stats参数,根据调用方比较参数的平均值,并且只有在平均值相同时才返回true。 例如:

.

integerinums [ ]={ 1,2,3,4,5 };

双精度编号[ ]={ 1.1,2.2,3.3,4.4,5.5 };

statsIOB=newstats(inums );

statsdob=newstats(dnums );

if(IOB.sameAVG(dob ) ) ) ) ) ) )。

system.out.println (averagesarethesame.';

else

system.out.println (averages differ.);

首先,sameAvg (创建方法看起来像是一个简单的问题。 Stats是通用的,average ()方法可以使用任何类型的Stats对象,因此创建sameAvg ()方法会更直观。 不幸的是,如果尝试声明Stats类型的参数,故障就会开始。 Stats是参数化类型,声明此类型的参数时为什么要指定Stats的类型参数?

乍一看,解决方案可能看起来如下,但其中的t用作类型参数:

.

publicbooleansameavg{

if(average(==ob.average ) )

返回真;

elsefalse;

}

这种尝试存在一个问题,即只有在其他Stats对象的类型与调用对象相同时才能工作。 例如,如果被调用方是Stats类型,则参数ob也必须是Stats类型。 不能用于比较Stats类型对象的平均值和Stats类型对象的平均值。 因此,该方式的适用范围狭窄,无法得到通用的“通用化”解决方案。

要创建泛型sameAvg ()方法,必须使用Java泛型的另一个属性:通配符(wildcard )参数。 通配符的参数是“? ”。 表示未知的类型。 使用通配符编写的sameAvg (方法如下:

.

publicbooleansameavg{

if(average(==ob.average ) )

返回真;

返回假;

}

.

其中Stats与所有Stats对象匹配,允许任何两个Stats对象比较它们的平均值。 让我们来看一个完整的例子:

打包测试;

公共类状态{

privateT[]nums;

公共状态(t ) o ) {

nums=o;

}

publicdoubleaverage

双精度和=0d;

for(inti=0; I

sum=nums[i].doubleValue (;

returnsum/nums.length;

}

publicbooleansameavg{

if(average(==ob.average ) )

返回真;

返回假;

}

}

打包测试;

publicclassWildcardDemo{

publicstaticvoidmain (字符串[ ] args ) {

integer [ ] inums={ 1,2,3,4,5 };

statsIOB=newstats(inums );

doublev=iob.average (;

system.out.println (iobaverageis ) v;

double [ ] dnums={ 1.1,2.2,3.3,4.4,5.5 };

statsdob=newstats(dnums );

doublew=dob.average (;

system.out.println (dobaverageis ) w;

float[]fnums={1.0f}

,2.0F,3.0F,4.0F,5.0F};

Stats fob = new Stats<>(fnums);

double x = fob.average();

System.out.println("fob average is " + x);

System.out.println("Averages of iob and dob");

if(iob.sameAvg(dob))

System.out.println("are the same.");

else

System.out.println("differ.");

System.out.println("Averages of iob and fob");

if(iob.sameAvg(fob))

System.out.println("are the same.");

else

System.out.println("differ.");

}

}

最后一点:通配符不会影响能够创建什么类型的Stats对象,理解这一点很重要。这是由Stats声明中的extends子句控制的。通配符只是简单地匹配所有有效的Stats对象。

有界通配符

可以使用与界定类型参数大体相同的方式界定通配符参数。对于创建用于操作类层次的泛型来说,有界通配符很重要。为了理解其中的原因,看下面这个例子:

package test;

public class TwoD {

int x,y;

TwoD(int a,int b) {

x = a;

y = b;

}

}

class ThreeD extends TwoD {

int z;

ThreeD(int a,int b,int c) {

super(a,b);

z = c;

}

}

class FourD extends ThreeD {

int t;

FourD(int a,int b,int c,int d) {

super(a, b, c);

t = d;

}

}

在这个类层次的顶部是TwoD,该类封装了二维坐标(X,Y坐标)。ThreeD派生自TwoD,该类添加了第3维,创建XYZ坐标。FourD派生自ThreeD,该类添加了第4维(时间),生成4维坐标。

下面显示的是泛型类Coords,该类存储了一个坐标数组:

class Coords {

T[] coords;

Coords(T[] o) {coords = o;}

}

注意Coords指定了一个由TwoD界定的类型参数。这意味着在Coords对象中存储的所有数据,将包含TwoD类或其子类的对象。

现在,假设希望编写一个方法,显示在Coords对象的cooreds数组中,每个元素的X和Y坐标。因为所有Coords对象的类型都至少有两个坐标XY,所以使用通配符很容易实现,如下例:

......

static void showXY(Coords>  c) {

System.out.println("X Y Coordinates:");

for(int i=0;i

System.out.println(c.coords[i].x + " " + c.coords[i].y);

}

}

......

因为Coords是有界的泛型类,并且将TwoD指定为上界,所以能够用于创建Coords对象的所有对象都将是TwoD类及其子类。因此,showXY()方法可以显示所有Coords对象的内容。

但是,如果希望创建显示ThreeD或FourD对象的X,Y和Z坐标的方法,该怎么办呢?麻烦是,并非所有Coords对象都有3个坐标,因为Coords对象只有X和Y坐标。所以,如何编写能够显示Coords和Coords对象的X、Y和Z坐标的方法,而又不会阻止该方法使用Coords对象呢?答案是使用有界的通配符参数。

有界的通配符为类型参数指定上界或下界,从而可以限制方法能够操作的对象类型。最常用的有界通配符的上界,是使用extends子句创建的,具体方式和用于创建有界类型的方式大体相同。

如果对象实际拥有3个坐标的话,使用有界通配符,可以很容易创建出显示Coords对象中X、Y和Z坐标的方法。例如下面的showXYZ()方法,如果Coords对象中存储的元素的实际类型是ThreeD(或派生自ThreeD),那么showXYZ()方法将显示这些元素的X、Y和Z坐标:

......

static void showXYZ(Coords extends ThreeD> c) {

System.out.println("X Y Z Coordinates:");

for(int i=0; i

System.out.println(c.coords[i].x + " " +

c.coords[i].y + " " +

c.coords[i].z);

}

}

注意,在参数c的声明中为通配符添加了extends子句。这表明“?”可以匹配任意类型,只要这些类型为ThreeD或其派生类即可。因此,extends子句建立了“?”能够匹配的上界。因为这个界定,可以使用对Coords或Coords类型对象的引用调用showXYZ()方法,但不能使用Coords类型的引用进行调用。如果试图使用Coords引用调用showXYZ()方法,就会导致编译时错误,从而确保了类型安全。

下面是演示使用有界通配符参数的整个程序:

package test;

class TwoD {

int x,y;

TwoD(int a,int b) {

x = a;

y = b;

}

}

class ThreeD extends TwoD {

int z;

ThreeD(int a,int b,int c) {

super(a,b);

z = c;

}

}

class FourD extends ThreeD {

int t;

FourD(int a,int b,int c,int d) {

super(a, b, c);

t = d;

}

}

package test;

public class Coords {

T[] coords;

Coords(T[] o) {coords = o;}

}

package test;

public class BoundedWildcard {

static void showXY(Coords> c) {

System.out.println("X Y Coordinates:");

for(int i=0;i

System.out.println(c.coords[i].x + " " + c.coords[i].y);

System.out.println();

}

static void showXYZ(Coords extends ThreeD> c) {

System.out.println("X Y ZCoordinates:");

for(int i=0;i

System.out.println(c.coords[i].x +" " +

c.coords[i].y + " " +

c.coords[i].z);

System.out.println();

}

static void showAll(Coords extends FourD> c) {

System.out.println("X Y Z T Coordinates: ");

for(int i=0; i

System.out.println(c.coords[i].x + " " +

c.coords[i].y + " " +

c.coords[i].z + " " +

c.coords[i].t);

System.out.println();

}

public static void main(String[] args) {

TwoD[] td = {

new TwoD(0,0),

new TwoD(7,9),

new TwoD(18,4),

new TwoD(-1,-23)

};

Coords tdlocs = new Coords<>(td);

System.out.println("Contents of tdlocs.");

showXY(tdlocs);

FourD[] fd = {

new FourD(1,2,3,4),

new FourD(6,8,14,8),

new FourD(22,9,4,9),

new FourD(3,-2,-23,17)

};

Coords fdlocs = new Coords<>(fd);

System.out.println("Contents of fdlocs.");

showXY(fdlocs);

showXYZ(fdlocs);

showAll(fdlocs);

}

}

一般来说,要为通配符建立上界,可以使用如下所示的通配符表达式:

 extends superclass>

其中,superclass是作为上界的类的名称。记住,这是一条包含子句,因为形成上界(由superclass指定的边界)的类也位于边界之内。

还可以通过为通配符添加一条super子句,为通配符指定下界。下面是一般形式:

 super subclass>

对于这种情况,只有subclass的超类是可接受参数。这是一条排除子句,因此与subclass指定的类不相匹配。

转自:http://my.oschina.net/fhd/blog/290174

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。