简体   繁体   English

Java 如何使泛型类型生效

[英]Java how to in-force Generic type

Below is code sample which I working on.下面是我正在处理的代码示例。

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));

This work as expected.这项工作符合预期。 I created another bean Pencil and forgot to implement KafkaRecord, and below code is still working.我创建了另一个 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 definition(kafka-clients-2.4.0.jar): ConsumerRecords 定义(kafka-clients-2.4.0.jar):

public class ConsumerRecords<K, V> implements Iterable<ConsumerRecord<K, V>> {
    //Behavior and getter/setters
}

Seems like below issue is happening because of generic erasure.由于通用擦除,似乎正在发生以下问题。 How can I fix this issue?我该如何解决这个问题?

After some more digging -经过更多的挖掘 -

Internal working from AbstractConfig.java- For setting deserilizer - AbstractConfig.java 的内部工作 - 用于设置 deserilizer -

  1. Its calling getClass(key) //key is coming from configuration它的调用 getClass(key) //key 来自配置
  2. It then create instance which it got from step 1.然后它创建从步骤 1 获得的实例。
  3. Then it cast to Class t(where t is Deserializer, in our case T should be KafkaRecord)然后它转换为 Class t(其中 t 是 Deserializer,在我们的例子中 T 应该是 KafkaRecord)

What I believe is that as run time type is not present, it allowing to set Deserializer<Pencil> to be set in first place.我相信,由于运行时类型不存在,它允许将Deserializer<Pencil>设置在首位。 So its able to deserialize it and able to assign it in ConsumerRecords<String, KafkaRecord> records , as type is not there.因此它能够反序列化它并能够在ConsumerRecords<String, KafkaRecord> records中分配它,因为类型不存在。

Sample GIT - https://github.com/jitendraVishnoi/kafka样品 GIT - https://github.com/jitendraVishnoi/kafka

Edit - I added antoher method in CustomExecutor.java -编辑 - 我在 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'}
  }

and which being called from-并且被称为-

ConsumerRecords<String, KafkaRecord> pencilRecords = pencilConsumer.poll(Duration.ofMillis(1000));
pencilRecords.forEach(bean -> playWithRecord(bean.value()));

first line works but second line break with ClassCastException.第一行有效,但第二行用 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)

If we update playWithRecord() to accept argument of type Object instead of KafkaRecord it works.如果我们更新 playWithRecord() 以接受 Object 类型的参数而不是 KafkaRecord 它可以工作。

Looks to me its happening as Type has been erased for -在我看来它正在发生,因为 Type 已被删除 -

ConsumerRecords<String, KafkaRecord> pencilRecords = pencilConsumer.poll(Duration.ofMillis(1000));

Actually, this is not a generic type / erasure problem at all.实际上,这根本不是通用类型/擦除问题。

Consider this:考虑一下:

SomeOtherComponent.process((Pencil) bean))

At compile time, the question is: can bean ... which we know implements KafakaRecord ... possibly be Pencil or some subclass of Pencil ?在编译时,问题是: bean ...我们知道实现KafakaRecord ...可能是Pencil还是Pencil的某个子类?

Now we know that it can't be an instance of Pencil , because Pencil does not implement KafakaRecord .现在我们知道它不可能是Pencil的实例,因为Pencil没有实现KafakaRecord But suppose I declare the following:但假设我声明以下内容:

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.如果我们在运行时records集合由作为ColouredPencil对象的KafkaRecord实例组成,那么它们Pencil的实例,因此类型转换不应该是编译时错误。

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.通常,从接口类型转换为 class 类型不是编译时错误,除非class 类型是final类型并且class 不直接或间接实现接口。


Is there any way I can report this issue while writing code.有什么方法可以在编写代码时报告这个问题。 That Pencil must implement KafkaRecord .那支铅笔必须实现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 .如果Pencil没有被声明为KafkaRecord的子类型,后者将给出编译错误。 That might be sufficient for your needs, especially if you can ensure that all of your deserializers implement KafkaDeserializer rather than Deserializer .这可能足以满足您的需求,特别是如果您可以确保所有反序列化器都实现KafkaDeserializer而不是Deserializer (I don't know enough about Kafka to know if this actually makes sense.) (我对 Kafka 了解得不够多,不知道这是否真的有意义。)

And another approach would be to simply declare Pencil as final .另一种方法是简单地将Pencil声明为final

It seems like this is happening because of generic erasure.由于通用擦除,这似乎正在发生。 https://coderanch.com/t/739304/java/Java-Generic-run-time https://coderanch.com/t/739304/java/Java-Generic-run-time

I had to discuss this in another forum as I was not getting much response here.我不得不在另一个论坛上讨论这个问题,因为我在这里没有得到太多回应。 @Stephen has already explained how to force java to report it compile time. @Stephen 已经解释了如何强制 java 报告它的编译时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM