英文:
Dynamically cast to a proper subclass from parent class in a method
问题
我有一个Java interface
叫做 PlatformConfigurable
。我还有两个类 PlatformProducerConfig
和 PlatformConsumerConfig
。
后来,我需要为这两个类添加一个共同的配置,将一个属性设置为空字符串:
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
if (platformConfig instanceof PlatformProducerConfig) {
PlatformProducerConfig oldConfig = (PlatformProducerConfig) platformConfig;
Map<String, String> additionalConfig = oldConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
}
else if (platformConfig instanceof PlatformConsumerConfig) {
PlatformConsumerConfig oldConfig = (PlatformConsumerConfig) platformConfig;
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
}
return platformConfig;
}
我进行了对生产者或消费者配置的强制类型转换,因为 PlatformConfigurable
接口中没有声明 .toBuilder()
或 .build()
方法,而且我无法访问修改该接口,我只能实现它。
我希望摆脱重复的代码:
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
我在考虑使用Lambda表达式,但我不确定如何做到这一点。
英文:
I have a Java interface
PlatformConfigurable
. I also have two classes PlatformProducerConfig
and PlatformConsumerConfig
.
Later on, I need to add a common config to both that sets a property to an empty string:
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
if (platformConfig instanceof PlatformProducerConfig) {
PlatformProducerConfig oldConfig = (PlatformProducerConfig) platformConfig;
Map<String, String> additionalConfig = oldConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
}
else if (platformConfig instanceof PlatformConsumerConfig) {
PlatformConsumerConfig oldConfig = (PlatformConsumerConfig) platformConfig;
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
}
return platformConfig;
}
I am casting to producer or consumer config because the PlatformConfigurable
interface doesn't have .toBuilder()
or .build()
methods declared in it, and I don't have access to modify the interface, as I can only implement it.
I would want to get rid of the duplicate code:
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return oldConfig.toBuilder().additionalProperties(newConfig).build();
I was thinking of using lambdas, but I am not 100% sure how to do it.
答案1
得分: 5
你可以像这样重构现有的代码:
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
if (!(platformConfig instanceof PlatformProducerConfig) && !(platformConfig instanceof PlatformConsumerConfig)) {
return platformConfig;
}
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(Collections::emptyMap));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
if (platformConfig instanceof PlatformProducerConfig) {
return ((PlatformProducerConfig) platformConfig).toBuilder().additionalProperties(newConfig).build();
}
return ((PlatformConsumerConfig) platformConfig).toBuilder().additionalProperties(newConfig).build();
}
更新
另一种方法是将与构建器相关的功能提取到单独的接口中,并在代码中使用它们:
// 1. 扩展现有的 `PlatformConfigurable`
public interface BuilderedPlatformConfigurable extends PlatformConfigurable {
ConfigPlatformBuilder toBuilder();
}
// 2. 提供具有共同实现的构建器接口
public interface ConfigPlatformBuilder {
Map<String, String> additionalProperties = new HashMap<>();
BuilderedPlatformConfigurable build();
default ConfigPlatformBuilder additionalProperties(Map<String, String> properties) {
this.additionalProperties.clear();
this.additionalProperties.putAll(properties);
return this;
}
}
// 3. 更新 PlatformConsumerConfig 类(类似地,更新 PlatformProducerConfig)
public class PlatformConsumerConfig implements BuilderedPlatformConfigurable {
private Map<String, String> additionalProperties = new HashMap<>();
@Override
public Map<String, String> additionalProperties() {
return additionalProperties;
}
public ConfigPlatformBuilder toBuilder() {
return new Builder();
}
public static class Builder implements ConfigPlatformBuilder {
public PlatformConsumerConfig build() {
PlatformConsumerConfig config = new PlatformConsumerConfig();
config.additionalProperties.putAll(this.additionalProperties);
return config;
}
}
}
// 4. 提供重载的方法
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
return platformConfig;
}
private PlatformConfigurable disableHostNameVerificationConfig(BuilderedPlatformConfigurable platformConfig) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(Collections::emptyMap));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return platformConfig.toBuilder().additionalProperties(newConfig).build();
}
注意:这只是您提供的代码的翻译部分,没有其他附加内容。
英文:
You could just refactor existing code like this:
private PlatfromConfigurable disableHostNameVerificationConfig(Platfromonfigurable platfromConfig) {
if (!(platformConfig instanceof PlatformProducerConfig) && !(platformConfig instanceof PlatformConsumerConfig)) {
return platformConfig;
}
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
if (platformConfig instanceof PlatformProducerConfig) {
return ((PlatformProducerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
}
return ((PlatformConsumerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
}
Update
Another approach could be to extract functionality related to the builder to separate interfaces and use them in this way:
// 1. extend existing `PlatformConfigurable`
public interface BuilderedPlatformConfigurable extends PlatformConfigurable {
ConfigPlatformBuilder toBuilder();
}
// 2. provide builder interface with common implementation
public interface ConfigPlatformBuilder {
Map<String, String> additionalProperties = new HashMap<>();
BuilderedPlatformConfigurable build();
default ConfigPlatformBuilder additionalProperties(Map<String, String> properties) {
this.additionalProperties.clear();
this.additionalProperties.putAll(properties);
return this;
}
}
// 3. update PlatformConsumerConfig class (similarly, PlatformProducerConfig)
public class PlatformConsumerConfig implements BuilderedPlatformConfigurable {
private Map<String, String> additionalProperties = new HashMap<>();
@Override
public Map<String, String> additionalProperties() {
return additionalProperties;
}
public ConfigPlatformBuilder toBuilder() {
return new Builder();
}
public static class Builder implements ConfigPlatformBuilder {
public PlatformConsumerConfig build() {
PlatformConsumerConfig config = new PlatformConsumerConfig();
config.additionalPropertie.putAll(this.additionalProperties);
return config;
}
}
}
// 4. provide overloaded method
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
return platformConfig;
}
private PlatformConfigurable disableHostNameVerificationConfig(BuilderedPlatformConfigurable platformConfig) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(Map::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return platformConfig.toBuilder().additionalProperties(newConfig).build();
}
答案2
得分: 4
使用泛型,对 Alex Rudenko 的答案 进行进一步扩展:
private <P extends PlatformConfigurable> P disableHostNameVerificationConfig(P platformConfig, BiFunction<P, Map<String, String>, P> appender) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return appender.apply(platformConfig, newConfig);
}
假设对于任何 PlatformConfigurable
的子类型(包括 PlatformConfigurable
本身),这样做是安全的。
然后可以这样调用:
disableHostNameVerificationConfig(
platformProducerConfig,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
disableHostNameVerificationConfig(
platformConsumerConfig,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
如果愿意,可以创建辅助方法来隐藏 BiFunction
:
private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}
private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}
实际上,我认为更好的方法是不使用泛型或 lambda 表达式:编写一个方法来创建更新后的映射:
private static Map<String, String> newConfig(PlatformConfigurable platformConfig) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = additionalConfig != null ? new HashMap<>(additionalConfig) : new HashMap<>();
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return newConfig;
}
然后只需编写两个重载方法:
private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
}
private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
}
英文:
Taking Alex Rudenko's answer a bit further, using generics:
private <P extends PlatformConfigurable> P disableHostNameVerificationConfig(P platformConfig, BiFunction<P, Map<String, String>, P> appender) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return appender.apply(platformConfig, newConfig);
}
This assumes that it is safe to do this for any subtype of PlatformConfigurable
(and PlatformConfigurable
itself).
Then invoke like:
disableHostNameVerificationConfig(
platformProducerConfig,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
disableHostNameVerificationConfig(
platformConsumerConfig,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
If you like, create helper methods to hide the BiFunction
s:
private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}
private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
return disableHostNameVerificationConfig(
platformConfigurable,
(p, config) -> p.toBuilder().setAdditionalConfig(config).build());
}
Actually, I think a better way to do it would be without generics or lambdas: write a method which creates an updated map:
private static Map<String, String> newConfig(PlatformConfigurable platformConfig) {
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = additionalConfig != null ? new HashMap<>(additionalConfig) : new HashMap<>();
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return newConfig;
}
and then just have two overloads:
private PlatformProducerConfig disableHostNameVerificationConfig(PlatformProducerConfig config) {
return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
}
private PlatformConsumerConfig disableHostNameVerificationConfig(PlatformConsumerConfig config) {
return config.toBuilder().setAdditionalConfig(newConfig(config)).build();
}
答案3
得分: 1
// 添加在Alex Rudenko的回答中的一点,并创建不同函数以添加不同接口的实现。
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
if ((platformConfig == null)) {
return platformConfig;
}
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return PlatformConfigurableObject(platformConfig, newConfig);
}
// 因此,您可以在不同的方法中处理所有实例,以后只需更改此方法即可添加后续的PlatformXCofing类。单一责任原则。
private PlatformConfigurable PlatformConfigurableObject(PlatformConfigurable platformConfig, Map<String, String> newConfig) {
if (platformConfig instanceof PlatformProducerConfig) {
return ((PlatformProducerConfig) platformConfig).toBuilder().additionalProperties(newConfig).build();
} else if (platformConfig instanceof PlatformConsumerConfig) {
return ((PlatformConsumerConfig) platformConfig).toBuilder().additionalProperties(newConfig).build();
} else {
return platformConfig;
}
}
英文:
Adding one thing in Alex Rudenko's answer, and making different function to add different implementations of interfaces.
private PlatformConfigurable disableHostNameVerificationConfig(PlatformConfigurable platformConfig) {
if ((platformConfig == null)) {
return platformConfig;
}
Map<String, String> additionalConfig = platformConfig.additionalProperties();
Map<String, String> newConfig = new HashMap<>(Optional.ofNullable(additionalConfig).orElseGet(ImmutableMap::of));
newConfig.put(SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
return PlatformConfigurableObject(platformConfig, newConfig);
}
So you can handle all instances in a different method, and whenever PlatfromXCofing classes are added later you only have to change this method. Single Responsibility Principle.
private PlatformConfigurable PlatformConfigurableObject(PlatformConfigurable platformConfig, Map<String, String> newConfig){
if (platformConfig instanceof PlatformProducerConfig) {
return ((PlatformProducerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
} else if (platformConfig instanceof PlatformConsumerConfig){
return ((PlatformConsumerConfig)platformConfig).toBuilder().additionalProperties(newConfig).build();
} else{
return platformConfig;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论