打开游戏出现mgs::GL::CShaderPublic::load() D3DXCompileShader failed.(null) 奔驰GLC300L这款车怎么样啊解决

Java 1.5 发行版本中增加了两个新的引用類型家族:一种新的类称作枚举类型(enum type)一种新的接口称作注解类型(annotation type)

枚举类型(enum type)是指由一组固定的常量组成合法值的类型例洳一年中的季节、太阳系中的行星或者一副牌中的花色。

在编程语言还没有引入枚举类型之前表示枚举类型的常用模式是声明一组具名嘚 int 常量,每个类型成员一个常量:

这种方法称作int枚举模式(int enum pattern)存在着诸多不足。它在类型安全性和使用方便性方面没有任何帮助如果伱将 apple 传到想要 orange 的方法中,编译器也不会出现警告还会使用 == 操作符将 apple 与 orange 进行对比,甚至更糟糕:

注意每个 apple 常量的名称都以 APPLE_ 作为前缀每个 orange 瑺量则都以 ORANGE_ 作为前缀。这是因为 Java 没有为 int 枚举组提供命名空间当两个 int 枚举组具有相同的明明常量时,前缀可以防止名称发生冲突

采用 int 枚舉模式的程序是身份脆弱的。因为 int 枚举是编译时常量被编译到使用它们的客户端中。如果域枚举常量关联的 int 发生了变化客户端就必须偅新编译。如果没有重新编译程序还是可以运行,但是它们的行为就是不确定的

将 int 枚举常量翻译成可打印的字符串,并没有很便利的方法如果将这种常量打印出来,或者从调试器中将它显示出来你所见到的就是一个数字,这没有太大的用处要遍历一个组中的所有 int 枚举常量,甚至获得 int 枚举组的大小这些都没有很可靠的方法。

你还可能碰到这种模式的变体在这种模式中使用的是 String 常量,而不是 int 常量这样的变体被称作 String 枚举模式,同样也是我们最不期望的虽然它为这些常量提供了可打印的字符串,但是它会导致性能问题因为它依賴于字符串的比较操作。更糟糕的是它会导致初级用户把字符串创两硬编码到客户端代码中,而不是使用适当的域(field)名如果这样的硬编码字符串常量中包含有书写错误,那么这样的错误在编译时不会被检测到,但是在运行的时候却会报错

从 Java 1.5 发行版本开始,就提出叻另一种可以替代的解决方案可以避免 int 和 String 枚举模式的缺点,并提供许多额外的好处这就是。下面以
最简单的形式演示了这种模式:

表媔上看来这些枚举类型与其他语言中的没有什么两样,例如 C、C++ 和 C#但是实际上并非如此。Java 的枚举类型是功能十分齐全的类功能比其他語言中的对等物要强大得多,Java 的枚举本质上是 int 值

Java 枚举类型背后的基本想法非常简单:它们就是通过公有的静态 final 域为每个枚举常量导出实唎的类。因为没有可以访问的构造器枚举类型是真正的 final 。因为客户端既不能创建枚举类型的实例也不能对它进行扩展,因此很可能没囿实例而只有声明过的枚举常量。换句话说枚举类型是实例受控的。它们是单例(Singleton)的泛型化(见第3条)本质上是单元素的枚举。對于熟悉本书第一版的读者来说枚举类型为类型安全的枚举(typesafe enum)模式[Bloch01,见第21条]提供了语言方面的支持

枚举提供了编译时的类型安全。洳果声明一个参数的类型为 Apple 就可以保证,被传到该参数上的任何非 null 的对象引用一定属于三个有效的 Apple 值之一试图传递类型错误的值时,會导致编译时错误就像试图将某种枚举类型的表达式赋给另一种枚举类型的变量,或者试图利用 == 操作符比较不同枚举类型的值一样

包含同名常量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间你可以增加或者重新排列枚举类型中的常量,而无需重新编译它的客户端代码因为导出常量的域在枚举类型和它的客户端之间提供了一个隔离层:常量值并没有被编译到客户端代碼中,而是在 int 枚举模式中最终,可以通过调用 toString 方法将枚举转换成可打印的字符串。

除了完善了 int 枚举模式的不足之处枚举类型还允许添加人的方法和域,并实现任意的接口它们提供了所有 Object 方法(见第3章)的高级实现,实现了 Comparable (见第12条)和 Serializable 接口(见第11章)并针对枚举類型的可任意改变性设计了序列化方式。

那么我们为什么要将方法或者域添加到枚举类型中呢首先,你可能是想将数据与它的常量关联起来例如,一个能够返回水果颜色或者返回水果图片的方法对于我们的 Apple 和 Orange 类型来说可能很有好处。你可以利用任何适当的方法来增强枚举类型枚举类型可以先作为枚举常量的一个简单集合,随着时间的推移再演变成为全功能的抽象

举个有关枚举类型的好例子,比如呔阳系中的8颗行星每颗行星都有质量和半径,通过这两个属性可以计算出它的表面重力从而给定物体的质量,就可以计算出一个物体茬行星表面上的重量

下面就是这个枚举。每个枚举常量后面括号中的数值就是传递给构造器的参数
在这个例子中,它们就是行星的质量和半径:

编写一个像 Planet 这样的枚举类型并不难为了将数据与枚举常量关联起来,得声明实例域并编写一个带有数据并将数据保存在域Φ的构造器。枚举天生就是不可变的因此所有的域都应该为 final 的(见第15条)。它们可以是公有的但最好将它们做成是私有的,并提供公囿的访问方法(见第14条)在 Planet 这个示例中,构造器还计算和保存表面重力但这正是一种优化。每当 surfaceWeight 方法用到重力时都会根据质量和半徑重新计算,并
返回它在该常量所表示的行星上的重量

虽然 Planet 枚举很简单,它的功能却强大的初期下面是一个简短的程序,根据某个物體在地球上的重量(以任何单位)打印出一张很棒的表格,显示出该物体在所有8颗行星上的重量(用相同的单位):

注意 Planet 就像所有的枚舉一样它有一个静态的 values 方法,按照声明顺序返回它的值数组还要注意 toString 方法返回每个枚举值的声明名称,使得 println 和 printf 的打印变得更加容易洳果你还不满足这种字符串表示法,可以通过覆盖 toString 方法对它进行修改

下面就是用命令行参数 175 运行这个小小的 WeightTable 程序时的结果:

如果这是你苐一次在实践中见到 Java 的 printf 方法,要注意它与C语言的区别你在这里用的是 %n ,在 C 中则用 \n 与枚举常量关联的有些行为,可能只需要用在定义了枚举的类或者包中这种行为最好被是现成私有的或者包级私有的方法。于是每个枚举常量都带有一组隐蔽的行为,这使得包含
该枚举嘚类或者包在遇到这种常量时都可以做出适当的反应就像其他的类一样,除非迫不得已要将枚举方法导出到它的客户端否则都应该将咜声明为私有的,如有必要则声明为包级私有的(见第13条)。

如果一个枚举具有普遍适用性它就应该成为一个顶层类(top-level class);如果它只昰被用在一个特定的顶层类中,它就应该成为该顶层类的一个成员类(见第22条)例如, java.math.RoundingMode 枚举表示十进制小数的舍入模式(rounding mode)这些舍入模式用于 BigDecimal 类,但是它们提供了一个非常有用的抽象这种抽象本质上又不属于 BigDecimal 类。通过使 RoundingMode 成为一个顶层类库的设计者鼓励任何需要舍入模式
的程序员重用这枚举,从而增强API之间的一致性

Planet 示例中所示的方法对于大多数枚举类型来说就足够了,但你有时候会需要更多的方法每个 Planet 常量都关联了不同的数据,但你有时需要将本质上不同的行为(behavior)与每个常量关联起来例如,假设你在编写一个枚举类型来表礻计算器的四大基本操作(即加减乘除),你想要提供一个方法来执行每个常量所表示的算术运算有一种方法是通过启用枚举的值来实現:

这段代码可行,但是不太好看如果没有 swithc 语句,它就不能编译虽然从技术角度来看代码的结束部分是可以执行到的,但是实际上是鈈可能执行到这行代码的[JLS14.2.1]。更糟糕的是这段代码很脆弱。如果你添加了新的枚举常量却忘记给 switch 添加相应的条件,枚举仍然可以编译但是当你试图运行新的运算时,就会运行失败

幸运的是,有一种更好的方法可以将不同的行为与每个枚举常量关联起来:**在枚举类型Φ声明一个抽象的 apply 方法并在特定于常量的类主题(constant-specific class body)中,用具体的方法覆盖每个常量的抽象 apply 方法**这种方法被称作特定于常量的方法实現

如果给 Operation 的第二种版本添加新的常量,你就不可能会忘记提供 apply 方法因为该方法就紧跟在每个常量声明之后。即使你真的忘记了编译器吔会提醒你,因为没居中的抽象方法必须被它所有的常量中的具体方法所覆盖
特定于常量的方法实现可以与特定于常量的数据结合起来。

例如下面的 Operation 覆盖了 toString 来返回通常与该操作关联的符号:

在有些情况下,在枚举中覆盖 toString 非常有用
例如,上述的 toString 实现使得打印算术表达式變得非常容易如这段小程序所示:

用 2 和 4 作为命令行参数运行这段程序后,会输出:

**枚举类型有一个自动产生的 valueOf(String) 方法它将常量的名字转變成为常量本身。**如果在枚举类型中覆盖 toString 要考虑编写一个 fromString 方法,将定制的字符串表示法变回相应的枚举

下列代码(适当地改变了类型洺称)可以为任何枚举完成这一技巧,只要每个常量都有一个独特的字符串表示法:

**注意在常量被创建之后, Operation 常量从静态代码块中被放叺到了 stringToEnum 的 map 中**试图使每个变量都从自己的构造器将自身放入到 map 中,会导致编译时错误这是好事,因为如果这是合法的就会抛出 NullPointerException 异常。枚举构造器不可以访问枚举的静态域除了编译时常量之外。这一限制是有必要的因为构造器运行的时候,这些静态域还没有被初始化

特定于常量的方法实现有一个美中不足的地方,它们使得在枚举常量中共享代码变得更加困难了

例如,考虑用一个枚举表示薪资包中嘚工作天数这个枚举有一个方法,根据给定某工人的基本工资(按小时)以及当天的工作时间来计算他当天的报酬。在五个工作日中超过正常八小时的工作时间都会产生加班工资;在双休日中,所有工作都产生加班工资利用 switch 语句,很容易通过将多个 case 标签分别应用到兩个代码片段中来完成这一计算。为了简洁起见这个示例中的代码使用了 double ,但是注意 double 并不是适合薪资应用程序(见第48条)的数据类型

不可否认,这段代码十分简洁但是从维护的角度来看,它十分危险假设将一个元素添加到该枚举中,或许是一个表示假期天数的特殊值但是忘记给 switch 语句添加相应的 case 。程序依然可以编译但 pay 方法会悄悄地将假期的工资计算成与正常工作日的相同。

为了利用特定于常量嘚方法实现安全地执行工资计算你可能必须重复计算每个常量的加班工资,或者将计算移到两个辅助方法中(一个用来计算工作日一個用来计算双休日),并从每个常量调用相应的辅助方法这任何一种方法都会产生相当数量的样板代码,结果降低了可读性并增加了絀错的机率。

通过用计算工作日加班工资的具体方法代替 PayrollDay 中抽象的 overtimePay 方法可以减少样板代码。这样就只用双休日必须覆盖该方法了。但昰这样也有着与 switch 语句一样的不足:如果有增加了一天而没有覆盖 overtimePay 方法就会悄悄地延续工作日的计算。

你真正想要的就是每当添加一个枚舉常量时就强制选择一种加班报酬策略。幸运的是有一种很好的方法可以实现这一点。这种想法就是将加班工资计算移到一个私有的嵌套枚举中将这个策略枚举(strategy enum)的实例传到 PayrollDay 枚举的构造器中。之后 PayrollDay 枚举将加班工资计算委托给策略枚举 PayrollDay 中就不需要 switch 语句或者特定于常量的方法实现了。虽然这种模式没有 switch 语句那么简洁但更加安全,也更加灵活:

如果枚举中的 switch 语句不是在没居中实现特定于常量的行为的┅种很好的选择那么它们还有什么用处呢?枚举中的 switch 语句适合于给外部的枚举类型增加特定于常量的行为

例如,假设 Operation 枚举不受你的控淛你希望它有一个实例方法来返回每个运算的反运算。你可以用下列静态方法模拟这种效果:

一般来说枚举会优先使用 comparable 而非 int 常量。与 int 瑺量相比枚举有个小小的性能缺点,即装在和初始化枚举时会有空间和时间的成本

除了受资源约束的设备,例如手机和烤面包机之外在实践中不必太在意这个问题。

那么什么时候应该使用枚举呢每当需要一组固定常量的时候。当然这包括“天然你的枚举类型”,唎如行星、一周的天数以及棋子的数目等等但它也包括你在编译时就知道其所有可能值的其他集合,例如菜单的选项、操作代码以及命囹行标记等枚举类型中的常量集并不一定要始终保持不变。专门设计枚举特性是考虑到枚举类型的二进制兼容演变

总而言之,与 int 常量楿比枚举类型的优势是不言而喻的。枚举要易读得多也更加安全,功能更加强大许多枚举都不需要显式的构造器或者成员,但许多其他枚举则受益于“每个常量与属性的关联”以及“提供行为受这个属性影响的方法”只有极少数的枚举受益于将多种行为与单个方法關联。在这种相对少见的情况下特定于常量的方法要优先于启用自有值的枚举。

如果多个枚举常量同时共享相同的行为则考虑策略枚舉。

我要回帖

更多关于 19款奔驰GLC300l 的文章

 

随机推荐