使用泛型类型类继承的意外方法暴露

Unintended method exposure using inheritance with a generics typed class

标题我已经尽力了。我想要完成的是带有依赖注入的分层模块化。这种设计模式是否好是另一个论坛的问题。

因为我使用依赖注入,所以我有接口/实现对。这是顶级接口:

public interface IConfiguration< T > where T : ConfigData

{

  T GetConfig();

}

public abstract class ConfigurationBase< T > : IConfiguration

{

  protected ConfigData Config { get; set; }



  public T GetConfig()

  {

    return Config as T;

  }

}

public interface IGeneralConfiguration : IConfiguration<GeneralConfigData>

public class GeneralConfiguration : ConfigurationBase<GeneralConfigData>, IGeneralConfiguration



public interface ILoginConfiguration : IConfiguration<LoginConfigData>, IGeneralConfiguration

public class LoginConfiguration : ConfigurationBase<LoginConfigData>, ILoginConfiguration



public interface IAppConfiguration : IConfiguration<AppConfigData>, ILoginConfiguration

public class AppConfiguration : ConfigurationBase<AppConfigData>, IAppConfiguration
var element = _appConfiguration.GetConfig().AppSpecificConfigElement;

其中 ConfigData 是一个简单的类,它公开 get/set 属性,如 LogLevelEnvironment

接口有一个基本实现:

public interface IConfiguration< T > where T : ConfigData

{

  T GetConfig();

}

public abstract class ConfigurationBase< T > : IConfiguration

{

  protected ConfigData Config { get; set; }



  public T GetConfig()

  {

    return Config as T;

  }

}

public interface IGeneralConfiguration : IConfiguration<GeneralConfigData>

public class GeneralConfiguration : ConfigurationBase<GeneralConfigData>, IGeneralConfiguration



public interface ILoginConfiguration : IConfiguration<LoginConfigData>, IGeneralConfiguration

public class LoginConfiguration : ConfigurationBase<LoginConfigData>, ILoginConfiguration



public interface IAppConfiguration : IConfiguration<AppConfigData>, ILoginConfiguration

public class AppConfiguration : ConfigurationBase<AppConfigData>, IAppConfiguration
var element = _appConfiguration.GetConfig().AppSpecificConfigElement;

现在是依赖注入部分!我有几个分层继承的接口/实现对。此外,它们的 protected Config 属性还在每个后续子类中公开了更多属性。这是我的接口/实现签名:

public interface IConfiguration< T > where T : ConfigData

{

  T GetConfig();

}

public abstract class ConfigurationBase< T > : IConfiguration

{

  protected ConfigData Config { get; set; }



  public T GetConfig()

  {

    return Config as T;

  }

}

public interface IGeneralConfiguration : IConfiguration<GeneralConfigData>

public class GeneralConfiguration : ConfigurationBase<GeneralConfigData>, IGeneralConfiguration



public interface ILoginConfiguration : IConfiguration<LoginConfigData>, IGeneralConfiguration

public class LoginConfiguration : ConfigurationBase<LoginConfigData>, ILoginConfiguration



public interface IAppConfiguration : IConfiguration<AppConfigData>, ILoginConfiguration

public class AppConfiguration : ConfigurationBase<AppConfigData>, IAppConfiguration
var element = _appConfiguration.GetConfig().AppSpecificConfigElement;

请注意,配置数据元素的继承方案是 ConfigDataGeneralConfigDataLoginConfigDataAppConfigData。配置数据元素只是在每个子项中公开更多特定于登录/应用程序等的属性(如 UsernameStartUri)。

现在,我可以在我的所有模块中使用这个配置概念。就依赖注入而言,解析 IGeneralConfigurationILoginConfigurationIAppConfiguration 将产生完全相同的实例。但是,现在通用模块只需要解析IGeneralConfiguration,特定于登录的模块只需要解析ILoginConfiguration,而特定于应用程序的模块可以解析IAppConfiugration,所有这些都可以访问其特定的部分配置数据他们试图处理的问题。这种模块化允许我创建更小的侧应用程序,这些应用程序可以重用来自主应用程序的模块,而无需进行大量自定义编码(例如,我可以重用登录模块而无需引用特定于应用程序的模块),只要我稍微改变我的依赖注册。

如果你到现在为止还和我在一起,这个模型的唯一问题是在我所有的子类中(继承自 ConfigurationBase<T>),它们都需要来自它们上面的接口的 ConfigData() 实现.这意味着 class LoginConfiguration 需要 public GeneralConfigData GetConfig() 的方法定义,class AppConfiguration 需要 public GeneralConfigData GetConfig()LoginConfigData GetConfig() 的方法定义。

很好。我这样做。现在,在我的应用程序特定模块中,出现编译器错误。在我的类字段定义中,我有 private IAppConfiguration _appConfiguration;。稍后在一个方法中,我引用了它:

public interface IConfiguration< T > where T : ConfigData

{

  T GetConfig();

}

public abstract class ConfigurationBase< T > : IConfiguration

{

  protected ConfigData Config { get; set; }



  public T GetConfig()

  {

    return Config as T;

  }

}

public interface IGeneralConfiguration : IConfiguration<GeneralConfigData>

public class GeneralConfiguration : ConfigurationBase<GeneralConfigData>, IGeneralConfiguration



public interface ILoginConfiguration : IConfiguration<LoginConfigData>, IGeneralConfiguration

public class LoginConfiguration : ConfigurationBase<LoginConfigData>, ILoginConfiguration



public interface IAppConfiguration : IConfiguration<AppConfigData>, ILoginConfiguration

public class AppConfiguration : ConfigurationBase<AppConfigData>, IAppConfiguration
var element = _appConfiguration.GetConfig().AppSpecificConfigElement;

编译器很困惑,说

the call is ambiguous between the following or properties 'IConfiguration.GetConfig()' and 'IConfiguration.GetConfig()'

为什么编译器没有看到类型是 IAppConfiguration 并定义对 AppConfigurationGetConfig() 的调用(其中 T 定义为 AppConfigData)?

有没有一种明显的方法可以使用我的方案来消除对 GetConfig() 的调用的歧义?


如果我理解正确,那么您刚才所做的是,除了无法自动解析的返回值之外,您有两个具有相同签名的方法。编译器不会(也不能)遍历从 ConfigData 派生的所有子类以确定 AppSpecificConfigElement 属于 AppConfiguration 并基于此选择重载 - 即使这样做,您也可以拥有多个具有 AppSpecificConfigElement 属性的类所以它不会更明智。您需要帮助编译器了解您的需求,方法是键入 _appConfiguration 到正确的类型,或者首先在语句中使用 ConfigData 的类型化后代而不是 var 然后获取属性。

在这两种情况下,我认为您严重过度设计,我建议您退后一步,重新考虑您的方法。正如@zaitsman 所说,这些对象应该是 POCO,并且具有不同的加载器(数据库、文件系统,...),实现简单的 Load/Save 接口,然后可以根据上下文传递给 DI。


相关推荐

  • Spring部署设置openshift

    Springdeploymentsettingsopenshift我有一个问题让我抓狂了三天。我根据OpenShift帐户上的教程部署了spring-eap6-quickstart代码。我已配置调试选项,并且已将Eclipse工作区与OpehShift服务器同步-服务器上的一切工作正常,但在Eclipse中出现无法消除的错误。我有这个错误:cvc-complex-type.2.4.a:Invali…
    2025-04-161
  • 检查Java中正则表达式中模式的第n次出现

    CheckfornthoccurrenceofpatterninregularexpressioninJava本问题已经有最佳答案,请猛点这里访问。我想使用Java正则表达式检查输入字符串中特定模式的第n次出现。你能建议怎么做吗?这应该可以工作:MatchResultfindNthOccurance(intn,Patternp,CharSequencesrc){Matcherm=p.matcher…
    2025-04-161
  • 如何让 JTable 停留在已编辑的单元格上

    HowtohaveJTablestayingontheeditedcell如果有人编辑JTable的单元格内容并按Enter,则内容会被修改并且表格选择会移动到下一行。是否可以禁止JTable在单元格编辑后转到下一行?原因是我的程序使用ListSelectionListener在单元格选择上同步了其他一些小部件,并且我不想在编辑当前单元格后选择下一行。Enter的默认绑定是名为selectNext…
    2025-04-161
  • Weblogic 12c 部署

    Weblogic12cdeploy我正在尝试将我的应用程序从Tomcat迁移到Weblogic12.2.1.3.0。我能够毫无错误地部署应用程序,但我遇到了与持久性提供程序相关的运行时错误。这是堆栈跟踪:javax.validation.ValidationException:CalltoTraversableResolver.isReachable()threwanexceptionatorg.…
    2025-04-161
  • Resteasy Content-Type 默认值

    ResteasyContent-Typedefaults我正在使用Resteasy编写一个可以返回JSON和XML的应用程序,但可以选择默认为XML。这是我的方法:@GET@Path("/content")@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})publicStringcontentListRequestXm…
    2025-04-161
  • 代码不会停止运行,在 Java 中

    thecodedoesn'tstoprunning,inJava我正在用Java解决项目Euler中的问题10,即"Thesumoftheprimesbelow10is2+3+5+7=17.Findthesumofalltheprimesbelowtwomillion."我的代码是packageprojecteuler_1;importjava.math.BigInteger;importjava…
    2025-04-161
  • Out of memory java heap space

    Outofmemoryjavaheapspace我正在尝试将大量文件从服务器发送到多个客户端。当我尝试发送大小为700mb的文件时,它显示了"OutOfMemoryjavaheapspace"错误。我正在使用Netbeans7.1.2版本。我还在属性中尝试了VMoption。但仍然发生同样的错误。我认为阅读整个文件存在一些问题。下面的代码最多可用于300mb。请给我一些建议。提前致谢publicc…
    2025-04-161
  • Log4j 记录到共享日志文件

    Log4jLoggingtoaSharedLogFile有没有办法将log4j日志记录事件写入也被其他应用程序写入的日志文件。其他应用程序可以是非Java应用程序。有什么缺点?锁定问题?格式化?Log4j有一个SocketAppender,它将向服务发送事件,您可以自己实现或使用与Log4j捆绑的简单实现。它还支持syslogd和Windows事件日志,这对于尝试将日志输出与来自非Java应用程序…
    2025-04-161