java - Java如何强制泛型类型
问题描述
下面是我正在处理的代码示例。
interface KafkaRecord {}
class Book implements KafkaRecord {
//some properties here with getter setter
}
class BookDeserializer implements Deserializer<Book> {
//Deserialiazing bytes to Book
}
// props is Properties which having key value pair for configuration.
Consumer<String, KafkaRecord> consumer = new KafkaConsumer<>(props);
ConsumerRecords<String, KafkaRecord> records =
consumer.poll(Duration.ofMillis(getTimeout()));
records.foreach(bean -> SomeComponent.process((Book) bean));
这项工作符合预期。我创建了另一个 bean Pencil,忘记实现 KafkaRecord,下面的代码仍然有效。
class Pencil {
//some getters and setters
}
class PencilDeserializer implements Deserializer<Pencil> {
//Deserialiazing bytes to Pencil
}
// props is Properties which having key value pair for configuration.
Consumer<String, KafkaRecord> consumer = new KafkaConsumer<>(props);
// Pencil does not implement KafkaRecord
ConsumerRecords<String, KafkaRecord> records =
consumer.poll(Duration.ofMillis(getTimeout()));
records.foreach(bean -> SomeOtherComponent.process((Pencil) bean));
ConsumerRecords 定义(kafka-clients-2.4.0.jar):
public class ConsumerRecords<K, V> implements Iterable<ConsumerRecord<K, V>> {
//Behavior and getter/setters
}
由于通用擦除,似乎正在发生以下问题。我该如何解决这个问题?
经过更多的挖掘 -
AbstractConfig.java 的内部工作 - 用于设置 deserilizer -
- 它的调用 getClass(key) //key 来自配置
- 然后它创建从步骤 1 获得的实例。
- 然后它转换为 t 类(其中 t 是 Deserializer,在我们的例子中 T 应该是 KafkaRecord)
我相信,由于运行时类型不存在,它允许Deserializer<Pencil>
首先设置。所以它能够反序列化它并能够将它分配给ConsumerRecords<String, KafkaRecord> records
,因为类型不存在。
示例 GIT - https://github.com/jitendraVishnoi/kafka
编辑 - 我在 CustomExecutor.java 中添加了另一个方法 -
private void playWithRecord(KafkaRecord record) {
System.out.println("Record class: " + record.getClass()); // Record class: class kafka.beans.Pencil
System.out.println("record instanceof KafkaRecord: " + (record instanceof KafkaRecord)); // record instanceof KafkaRecord: false
System.out.println("record instanceof Pencil: " + (record instanceof Pencil)); // record instanceof Pencil: true
System.out.println("toString() :" + record); // toString() :Pencil{color='Red'}
}
并且被称为-
ConsumerRecords<String, KafkaRecord> pencilRecords = pencilConsumer.poll(Duration.ofMillis(1000));
pencilRecords.forEach(bean -> playWithRecord(bean.value()));
第一行有效,但第二行用 ClassCastException 换行。
Exception in thread "Thread-0" java.lang.ClassCastException: kafka.beans.Pencil cannot be cast to kafka.beans.KafkaRecord
at CustomExecutor.lambda$run$0(CustomExecutor.java:28)
at java.lang.Iterable.forEach(Unknown Source)
at CustomExecutor.run(CustomExecutor.java:28)
at java.lang.Thread.run(Unknown Source)
如果我们更新 playWithRecord() 以接受 Object 类型的参数而不是 KafkaRecord 它可以工作。
在我看来它正在发生,因为 Type 已被删除 -
ConsumerRecords<String, KafkaRecord> pencilRecords = pencilConsumer.poll(Duration.ofMillis(1000));
解决方案
实际上,这根本不是通用类型/擦除问题。
考虑一下:
SomeOtherComponent.process((Pencil) bean))
在编译时,问题是:bean
我们知道的 ... 可以实现KafakaRecord
... 可能是?Pencil
的子类或某个子类Pencil
?
现在我们知道它不能是 的实例Pencil
,因为Pencil
没有实现KafakaRecord
。但假设我声明以下内容:
class ColoredPencil extends Pencil implements KafkaRecord {
...
}
If our records
collection at runtime consisted of KafkaRecord
instances that were ColouredPencil
objects, then they would be instances of Pencil
, so the type cast should not be a compile time error.
In general, a cast from an interface type to a class type is NOT a compile time error, unless the class type is final
and the class does not directly or indirectly implement the interface.
Is there any way I can report this issue while writing code. That Pencil must implement
KafkaRecord
.
Not with the code as written.
You could try this approach:
interface <T extends KafkaRecord> KafkaDeserializer extends Deserializer<T> {}
class PencilDeserializer implements KafkaDeserializer<Pencil> {
// Deserialiazing bytes to Pencil
}
The latter will give a compilation error if Pencil
hasn't been declared as a subtype of KafkaRecord
. That might be sufficient for your needs, especially if you can ensure that all of your deserializers implement KafkaDeserializer
rather than Deserializer
. (I don't know enough about Kafka to know if this actually makes sense.)
And another approach would be to simply declare Pencil
as final
.
推荐阅读
- javascript - 链接以显示具有表格行的功能
- angular - 使用 Angular Reactive Forms,是否可以为每个验证器设置单独的“updateOn”?
- linux - 使用 SIGSTOP 和 SIGCONT 在 Bash 中处理并发文件写入
- css - 背景图像在使用 flexbox 的移动屏幕中无法正确调整大小
- spring - Spring Boot PasswordEncoder.matches 始终为 false
- powershell - 如何在 Windows Server 2012 中使用 Powershell 5 检查 Windows 服务的启动类型是“自动”还是“自动延迟”
- swift - 如何在 swift 中将正文添加到 https 请求
- python - 使用 Python/BeautifulSoup 从 HTML 中提取与文本一致的链接
- javascript - 如何将 Material-UI Autocomplete 与 react-virtualized 一起使用?
- latex - 如何在 MathJax 中绘制一个矩形?