在调用超级构造函数之前检查变量的有效性

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 { }

实现 MediaFormatMusicFormat,允许您指定支持的音乐格式:

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 不可扩展,这只是一个设计选择。