我可以在抽象 class 中创建一个方法,它为实例化 class 构造一个实例吗?

Can I make a method in an abstract class which constructs an instance for the instantiating class?

我有两个 class 扩展一个抽象模型。两个 class 都实现了一个名为 instance() 的方法,以基本上确保任何时候都只有一个 class 的实例。两个 classes 的 instance() 结构完全相同,所以我认为将它向上移动一个级别到抽象 class 会很好。但是,该方法调用实例化 class' 默认构造函数。是否可以从抽象 class 中调用此构造函数?如果是这样怎么办?还有哪些其他方法可以推广此方法?

简化示例类

我有一个模型的摘要 class,看起来像

public abstract class Models{
    public List<Model> models = new ArrayList<Model>();

    /** load the different models, with the models with pre-trained model*/
    public abstract void load();
}

还有两个像这样的实例化 classes

 public class PageLanguageModels extends Models {
     /** ensure we only call one of them */
     protected static PageLanguageModels _instance = null;
     static Logger logger = Logger.getLogger(ProductLanguageModels.class.getName());

     public static synchronized PageLanguageModels instance() {
         if (_instance == null) {
             try {
                 _instance = new PageLanguageModels();
                 _instance.load();
             } catch (Exception e) {
                 logger.log(Level.SEVERE, "Couldn't load language models.", e);
             }
         }

         return _instance;
     }

     /** load the different models, with the models with pre-trained model*/
     @Override
     public void load() {
         models.clear();
         models.add(new BOWModel());
     }
 }

 public class ProductLanguageModels extends Models {
     /** ensure we only call one of them */
     protected static ProductLanguageModels _instance = null;
     static Logger logger = Logger.getLogger(ProductLanguageModels.class.getName());

     public static synchronized ProductLanguageModels instance() {
         if (_instance == null) {
             try {
                 _instance = new ProductLanguageModels();
                 _instance.load();
             } catch (Exception e) {
                 logger.log(Level.SEVERE, "Couldn't load language models.", e);
             }
         }

         return _instance;
     }

     /** load the different models, with the models with pre-trained model*/
     @Override
     public void load() {
         models.clear();
         models.add(new Word2VecModel());
     }
 }

尝试的方法

我试过使用工厂方法模式,但这不起作用,因为实例是静态方法,不能从静态方法调用抽象工厂方法。

Cannot make a static reference to the non-static method makeModels() from the type Models

public abstract class Models{

    /** load the different models, with the models with pre-trained model*/
    public abstract void load();

    //Factory method
    public abstract Models makeModels();

    // Instance code moved up from instanciating classes        
    protected static Models _instance = null;
    static Logger logger = Logger.getLogger(Models.class.getName());

    public static synchronized Models instance() {
        if (_instance == null) {
            try {
                _instance = makeModels();
                _instance.load();
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Couldn't load language models.", e);
            }
        }

        return _instance;
    }
}

是的,但您可能希望将其提取到单独的 class。

我认为您不能将所有实例化逻辑移至父级 class,因为它的静态性质和类型擦除方面的问题,但您肯定可以组织代码以使其可重用.我为您写了一个简单的 copy/paste 示例,通过稍微更改您的设计,重点放在实例化部分,所以我省略了一些属性和日志代码:

一个接口

public interface Models {
    void load();
}

抽象实现

public abstract class BaseModels implements Models {

    protected static <T extends Models> T instance(Class<T> type, T candidate) {
        if (candidate == null) {
            try {
                candidate = type.newInstance();
                candidate.load();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return candidate;
    }
}

具体实现

public class HeroModels extends BaseModels {

    static HeroModels instance;

    public static HeroModels instance() {
        instance = instance(HeroModels.class, instance);
        return instance;
    }

    @Override
    public void load() {
        System.out.println("Loading HeroModels...");
    }
}

一个简单的测试用例

public class TestDrive {

    @Test
    public void testEquality() {

        HeroModels a1 = HeroModels.instance();
        HeroModels a2 = HeroModels.instance();

        Assert.assertEquals(a1, a2);

        System.out.println("a1: " + a1);
        System.out.println("a2: " + a2);
    }
}

测试用例输出

Loading HeroModels... a1: HeroModels@736e9adb a2: HeroModels@736e9adb

从输出中您可以看到 HeroModels class 只加载了一次。 BaseModel class 中的 instance(...) 方法受到保护,以明确它旨在供儿童使用,儿童可能有自己的静态实例属性。