在调用超级构造函数之前检查变量的有效性
Checking the validity of a variable before calling the super constructor
所以我正在编写一些涉及扩展 class 的代码,我之前写过在其中创建文件并使用构造函数命名文件,该构造函数接受名称和 long 类型的大小。在原来的 class 中,我在构造函数中验证了输入的文件名包含一个“.”。字符,但不需要文件的特定扩展名。对于我正在编写的这个新 class,我要求名称的扩展名是“.mp3”。但是,我的编译器不喜欢在超级构造函数之前进行验证。
这是我当前的代码:
public class Song extends DigitalMedia{
private String artist;
private String album;
private String name;
private long size;
public Song(String aName, long aSize, String aArtist, String aAlbum){
super(aName, aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
在我创建该构造函数之前,有什么方法可以验证 "aName" 是否包含“.mp3”?
我不能说这是否是设计程序的最佳方式,但您可以在 super
个参数之一中调用验证器方法:
public Song(String aName, long aSize, String aArtist, String aAlbum){
super(validateName(aName), aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
private static String validateName(String name) {
if (whatever) {
throw new Whatever();
}
return name;
}
另一种解决方案是通过 built-in 类型检查来执行您的规则。
您可以创建 MediaFormat
:
interface MediaFormat { }
实现 MediaFormat
的 MusicFormat
,允许您指定支持的音乐格式:
enum MusicFormat implements MediaFormat {
MP3("mp3");
private final String format;
MusicFormat(String format) {
this.format = format;
}
@Override
public String toString() {
return format;
}
}
DigitalMedia
可以由 MediaFormat
:
组成
class DigitalMedia {
private final MediaFormat format;
private final String name;
public DigitalMedia(String name, MediaFormat format) {
this.name = name;
this.format = format;
}
}
Song
可以接受 MusicFormat
:
class Song {
public Song(String name, MusicFormat format) {
super(name, format);
}
}
这将强制用户使用 MusicFormat
中指定的任何内容,避免所有那些讨厌的检查。然后,您可以公开 String
方法 returns name + "." + format
从继承 stand-point,sub-class 不应该比它的超级 class.
更严格
但是如果你想让 sub-class 的实例化更受限制,你可以使构造函数 private
并提供一个首先进行验证的工厂方法。
public class Song extends DigitalMedia {
private String artist;
private String album;
private String name;
private long size;
private Song(String aName, long aSize, String aArtist, String aAlbum) {
super(aName, aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
public static Song makeSong(String aName, long aSize, String aArtist, String aAlbum) {
//... validation code
return new Song(aName, aSize, aArtist, aAlbum);
}
...
}
您不是限制类型本身,而是使用封装来强制执行不变量。
代码执行达到 constructor
意味着对象处于活动状态,现在可以对其 states
(字段)进行初始化。
classA.java
的对象也可以称为A.java
的超class的对象。在 class A.java
初始化状态之前,对象从超级 class 继承属性和特征是有意义的。 super class 完成初始化后,class A.java
有机会做初始化。
如果 super class 中没有参数构造函数,则隐式调用 super class 的构造函数,否则您需要调用 super class 的任何一个参数化构造函数] 明确地。
如果条件在 constructor
中失败,您想做什么?您可以选择抛出异常,但仍然会创建对象,您可以通过覆盖 finalize()
方法并检查 this
对象来验证相同的情况。您可能希望通过调用 System.gc()
来影响垃圾收集器,以便代码执行更快到达 finalize()
方法。
建议的解决方案
在调用构造函数之前,您应该验证构造函数的参数。如果你想将它封装在你的 class 中,那么你可以选择添加一个非私有静态方法(你可能希望将其命名为 getInstance()
)创建并返回 class 的对象Song
。在这种情况下,您可以将构造函数设为私有。请注意,这将使您的 class 不可扩展,这只是一个设计选择。
所以我正在编写一些涉及扩展 class 的代码,我之前写过在其中创建文件并使用构造函数命名文件,该构造函数接受名称和 long 类型的大小。在原来的 class 中,我在构造函数中验证了输入的文件名包含一个“.”。字符,但不需要文件的特定扩展名。对于我正在编写的这个新 class,我要求名称的扩展名是“.mp3”。但是,我的编译器不喜欢在超级构造函数之前进行验证。
这是我当前的代码:
public class Song extends DigitalMedia{
private String artist;
private String album;
private String name;
private long size;
public Song(String aName, long aSize, String aArtist, String aAlbum){
super(aName, aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
在我创建该构造函数之前,有什么方法可以验证 "aName" 是否包含“.mp3”?
我不能说这是否是设计程序的最佳方式,但您可以在 super
个参数之一中调用验证器方法:
public Song(String aName, long aSize, String aArtist, String aAlbum){
super(validateName(aName), aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
private static String validateName(String name) {
if (whatever) {
throw new Whatever();
}
return name;
}
另一种解决方案是通过 built-in 类型检查来执行您的规则。
您可以创建 MediaFormat
:
interface MediaFormat { }
实现 MediaFormat
的 MusicFormat
,允许您指定支持的音乐格式:
enum MusicFormat implements MediaFormat {
MP3("mp3");
private final String format;
MusicFormat(String format) {
this.format = format;
}
@Override
public String toString() {
return format;
}
}
DigitalMedia
可以由 MediaFormat
:
class DigitalMedia {
private final MediaFormat format;
private final String name;
public DigitalMedia(String name, MediaFormat format) {
this.name = name;
this.format = format;
}
}
Song
可以接受 MusicFormat
:
class Song {
public Song(String name, MusicFormat format) {
super(name, format);
}
}
这将强制用户使用 MusicFormat
中指定的任何内容,避免所有那些讨厌的检查。然后,您可以公开 String
方法 returns name + "." + format
从继承 stand-point,sub-class 不应该比它的超级 class.
更严格但是如果你想让 sub-class 的实例化更受限制,你可以使构造函数 private
并提供一个首先进行验证的工厂方法。
public class Song extends DigitalMedia {
private String artist;
private String album;
private String name;
private long size;
private Song(String aName, long aSize, String aArtist, String aAlbum) {
super(aName, aSize);
setArtist(aArtist);
setAlbum(aAlbum);
}
public static Song makeSong(String aName, long aSize, String aArtist, String aAlbum) {
//... validation code
return new Song(aName, aSize, aArtist, aAlbum);
}
...
}
您不是限制类型本身,而是使用封装来强制执行不变量。
代码执行达到 constructor
意味着对象处于活动状态,现在可以对其 states
(字段)进行初始化。
classA.java
的对象也可以称为A.java
的超class的对象。在 class A.java
初始化状态之前,对象从超级 class 继承属性和特征是有意义的。 super class 完成初始化后,class A.java
有机会做初始化。
如果 super class 中没有参数构造函数,则隐式调用 super class 的构造函数,否则您需要调用 super class 的任何一个参数化构造函数] 明确地。
如果条件在 constructor
中失败,您想做什么?您可以选择抛出异常,但仍然会创建对象,您可以通过覆盖 finalize()
方法并检查 this
对象来验证相同的情况。您可能希望通过调用 System.gc()
来影响垃圾收集器,以便代码执行更快到达 finalize()
方法。
建议的解决方案
在调用构造函数之前,您应该验证构造函数的参数。如果你想将它封装在你的 class 中,那么你可以选择添加一个非私有静态方法(你可能希望将其命名为 getInstance()
)创建并返回 class 的对象Song
。在这种情况下,您可以将构造函数设为私有。请注意,这将使您的 class 不可扩展,这只是一个设计选择。