克隆泛型的类型安全方法
Type safe way for cloning generics
我有一个超级class:
@Getter
@SuperBuilder(toBuilder = true)
public class Animal {
private Integer size;
}
和几个子class它
@Getter
@SuperBuilder(toBuilder = true)
public class Dog extends Animal{
private String color;
}
如您所见,以上两个 class 都是不可变的。我还有一个通用的 class 可以计算动物的任何子class。我在这里创建了一个新的动物实例,增加了尺寸。
public class GrowAnimal<T extends Animal> {
public T execute(T animal) {
// Here I need to explicitly cast to T
T newAnimal = (T) animal.toBuilder()
.size(animal.getSize() + 1)
.build();
return newAnimal;
}
}
这种方法存在几个问题:
- 我不能让 Animal 成为抽象的 class
- 如果我有更多的变形 class 像 GrowAnimal
它会变得非常不安全
- 如果有人添加了一个新的 subclass animal 并忘记向其添加 SuperBuilder,GrowAnimal 将只复制 super-class 属性。
有没有更好的克隆T的方法?我想在创建的新实例中保留动物的所有其他属性(例如,狗的颜色)不变。我可以使用 setter 来改变传入的对象,而不是克隆,但这是一种代码味道。
您不必使用泛型。 execute
方法可以接收 Animal
而不是 T
并且应该可以解决它。
您的方法没有(类型)安全的方法。根本原因是您不能对具有“纯Java”含义的子类强制执行@SuperBuilder(toBuilder = true)
。
如果一个子类只有 @SuperBuilder(toBuilder = false)
甚至根本没有 @SuperBuilder
,这段代码可以正常编译。但是,在该子类的实例上调用 toBuilder()
不会 return 该类型的构建器,而是 @SuperBuilder(toBuilder = true)
最近的超类的构建器。因此,您显式转换为 T
被编译器标记为不安全是有充分理由的。
如果只是您控制的代码,您可以编写单元测试以确保 Animal
的所有子类也具有 @SuperBuilder(toBuilder = true)
。可以支持您编写此类测试的框架是 ArchUnit。使用它,您可以编写 classes().that().areAssignableTo(Animal.class).should()
之类的内容并附加您的条件。请记住,@SuperBuilder
注释本身在编译期间被删除,因此您必须检查 lombok-generated 代码是否存在。
如果 Animal
可以通过 third-party 代码扩展,您可以在运行时检查是否所有子类都有 @SuperBuilder(toBuilder = true)
.
或者您只是忍受这样一个事实,即您在 GrowAnimal
中的代码可能会抛出 ClassCastException
s。
我有一个超级class:
@Getter
@SuperBuilder(toBuilder = true)
public class Animal {
private Integer size;
}
和几个子class它
@Getter
@SuperBuilder(toBuilder = true)
public class Dog extends Animal{
private String color;
}
如您所见,以上两个 class 都是不可变的。我还有一个通用的 class 可以计算动物的任何子class。我在这里创建了一个新的动物实例,增加了尺寸。
public class GrowAnimal<T extends Animal> {
public T execute(T animal) {
// Here I need to explicitly cast to T
T newAnimal = (T) animal.toBuilder()
.size(animal.getSize() + 1)
.build();
return newAnimal;
}
}
这种方法存在几个问题:
- 我不能让 Animal 成为抽象的 class
- 如果我有更多的变形 class 像 GrowAnimal 它会变得非常不安全
- 如果有人添加了一个新的 subclass animal 并忘记向其添加 SuperBuilder,GrowAnimal 将只复制 super-class 属性。
有没有更好的克隆T的方法?我想在创建的新实例中保留动物的所有其他属性(例如,狗的颜色)不变。我可以使用 setter 来改变传入的对象,而不是克隆,但这是一种代码味道。
您不必使用泛型。 execute
方法可以接收 Animal
而不是 T
并且应该可以解决它。
您的方法没有(类型)安全的方法。根本原因是您不能对具有“纯Java”含义的子类强制执行@SuperBuilder(toBuilder = true)
。
如果一个子类只有 @SuperBuilder(toBuilder = false)
甚至根本没有 @SuperBuilder
,这段代码可以正常编译。但是,在该子类的实例上调用 toBuilder()
不会 return 该类型的构建器,而是 @SuperBuilder(toBuilder = true)
最近的超类的构建器。因此,您显式转换为 T
被编译器标记为不安全是有充分理由的。
如果只是您控制的代码,您可以编写单元测试以确保 Animal
的所有子类也具有 @SuperBuilder(toBuilder = true)
。可以支持您编写此类测试的框架是 ArchUnit。使用它,您可以编写 classes().that().areAssignableTo(Animal.class).should()
之类的内容并附加您的条件。请记住,@SuperBuilder
注释本身在编译期间被删除,因此您必须检查 lombok-generated 代码是否存在。
如果 Animal
可以通过 third-party 代码扩展,您可以在运行时检查是否所有子类都有 @SuperBuilder(toBuilder = true)
.
或者您只是忍受这样一个事实,即您在 GrowAnimal
中的代码可能会抛出 ClassCastException
s。