【问题标题】:In Java, can I store a class type in variable, where the class is some subclass of an abstract class?在Java中,我可以将类类型存储在变量中,该类是抽象类的某个子类吗?
【发布时间】:2022-01-04 19:13:28
【问题描述】:

这个问题是因为这个cf4j example code而出现的。特别注意,它为每个被评估的推荐实例创建一个新的 QualityMeasure 对象,并且 如果我们想切换我们的评估方法/使用的 QualityMeasure 子类(当前RMSE),我们将不得不搜索并- 替换每个 QualityMeasure 构造函数

    // Evaluate PMF Recommender
    plot.addSeries("PMF");
    for (int factors : NUM_FACTORS) {
      Recommender pmf = new PMF(datamodel, factors, NUM_ITERS, RANDOM_SEED);
      pmf.fit();

      QualityMeasure rmse = new RMSE(pmf);  // NEED TO EDIT THIS...
      double rmseScore = rmse.getScore();
      plot.setValue("PMF", factors, rmseScore);
    }

    // Evaluate BNMF Recommender
    plot.addSeries("BNMF");
    for (int factors : NUM_FACTORS) {
      Recommender bnmf = new BNMF(datamodel, factors, NUM_ITERS, 0.2, 10, RANDOM_SEED);
      bnmf.fit();

      QualityMeasure rmse = new RMSE(bnmf);  // AND THIS...
      double rmseScore = rmse.getScore();
      plot.setValue("BNMF", factors, rmseScore);
    }

    // Evaluate BiasedMF Recommender
    plot.addSeries("BiasedMF");
    for (int factors : NUM_FACTORS) {
      Recommender biasedmf = new BiasedMF(datamodel, factors, NUM_ITERS, RANDOM_SEED);
      biasedmf.fit();

      QualityMeasure rmse = new RMSE(biasedmf);  // AND ALSO THIS...
      double rmseScore = rmse.getScore();
      plot.setValue("BiasedMF", factors, rmseScore);
    }

是否可以定义一个(固定的)变量来指定我将在整个过程中使用哪种子类?

   private static final QualityMeasureSubclass QM = RMSE;  // Can easily switch to MAE, MSE, etc.

   QualityMeasure qm = new QM(pmf);  // The constructor called depends on the subclass chosen above.
   double qmScore = qm.getScore();
   plot.setValue("PMF", factors, qmScore);

我确实看到我可以把通用代码分解出来,所以我可以在那个地方调整它,但我不知道上面是否可行。

    private static double getQualityMeasureValue(Recommender rec) {
        QualityMeasure qm = new RMSE(rec);  // Only place we need to edit if we want to switch.
        return qm.getScore();
    }

    // Evaluate PMF Recommender
    plot.addSeries("PMF");
    for (int factors : NUM_FACTORS) {
      Recommender pmf = new PMF(datamodel, factors, NUM_ITERS, RANDOM_SEED);
      pmf.fit();
      plot.setValue("PMF", factors, getQualityMeasureValue(pmf));
    }

编辑:清理了最后一段代码(不需要传入绘图)。

【问题讨论】:

  • 您可以随心所欲地进行 - 即定义一次子类,但您需要一些使用反射的实用方法来完成工作,我不确定您是否准备好或愿意走这条路。搜索一下如何使用反射。

标签: java oop


【解决方案1】:

对于Factory 来说,这听起来像是一份完美的工作。假设 Java 9,您的构造函数都可以映射到 Function<Recommender, QualityMeasure>

Map<Class<?>, Function<Recommender, QualityMeasure>> factories = Map.ofEntries(
        Map.entry(RMSE.class, RMSE::new),
        Map.entry(Other.class, Other::new));

然后您可以定义一个简单的函数来调用,而不是直接调用构造函数表达式:

QualityMeasure newQM(Class<?> QM, Recommender rec) {
    return factories.get(QM).apply(rec);
}

随意将键从 Class&lt;?&gt; 更改为 String 或将值更改为具有附加(转换)逻辑的实际 lambda。

为避免在尝试新建未知类型时出现 NPE,您可以返回 Null Object

factories.getOrDefault(cls, s -> null).apply(rec);

但请注意,在这种情况下apply 将返回null,这可能会导致进一步的问题。根据您的整体结构,在这种情况下尽早失败可能会更好:

var ctor = Objects.requireNonNull(
        factories.get(cls),
        () -> "No ctor registered for class " + cls);
ctor.apply(rec);

【讨论】:

  • 我认为这可能是解决方案的一部分,但它无法解决最后的部分,例如QualityMeasure qm = new QM(pmf);,除非您为所有用途定义工厂,这似乎不符合 OP 的愿望方便更改。
  • @Kayaman 谢谢,我已经扩展了我的答案。希望现在更清楚一点。
  • @Bohemian:我的回答假设所有可能的QualityMeasure 实现都是事先知道的,是的。如果不是这样,就不得不求助于 Java 反射的形式,这很容易变得复杂得多。我已经回答了一个可能的解决方案的建议。其他任何人都可以提出不同或更好的解决方案。
  • @knittl 这不是被人知道的问题。问题是每个子类的每次使用都需要一个函数,并且有多种用途 - 您的工厂映射只处理一种用途。
  • @Kayaman 我没有关注。如果我翻译 OP 的建议/预期代码 (new QM(pmf)),我的代码可以用作 newQM(QM, pmf)。在最初的一次性更改以使代码可扩展之后,我看不出这需要更多更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-13
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多