【问题标题】:Can I make a method in an abstract class which constructs an instance for the instantiating class?我可以在为实例化类构造实例的抽象类中创建一个方法吗?
【发布时间】:2015-12-02 20:04:26
【问题描述】:

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

简化示例类

我有一个模型的抽象类,看起来像

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();
}

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

 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());
     }
 }

尝试的方法

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

无法对非静态方法 makeModels() 进行静态引用 来自模型类型

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

【问题讨论】:

  • 不能在抽象类中创建静态方法。在您的情况下使用 Factory 或 Builder 类可能会有所帮助。附带说明一下,要创建不同步的单例,您可以使用 Holder 模式。

标签: java constructor abstract-class


【解决方案1】:

我认为您不能将所有实例化逻辑移至父类,因为它的静态性质和类型擦除相关的问题,但您当然可以组织您的代码以使其可重用。我为您编写了一个简单的复制/粘贴示例,只需稍微更改您的设计,重点放在实例化部分,所以我省略了一些属性和日志记录代码:

界面

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 类只加载了一次。 BaseModel 类中的 instance(...) 方法被设置为受保护,以明确它是供孩子使用的,这些孩子可能有自己的静态实例属性。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 你能补充一点解释吗?对于您的原始答案,我可以搜索“工厂模式”。但是在这里我没有太多关于如何探索您的建议的内容。
    猜你喜欢
    • 2020-11-21
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 2022-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多