Rumah > Java > javaTutorial > teks badan

Mari analisa generik Java dan kad bebas generik bersama-sama

WBOY
Lepaskan: 2022-10-10 15:55:55
ke hadapan
1947 orang telah melayarinya

Artikel ini membawa anda pengetahuan yang berkaitan tentang java, yang terutamanya memperkenalkan isu yang berkaitan dengan kad bebas generik dan generik, kerana sokongan generik ialah sokongan pengkompil, bait Apabila kod dimuatkan ke dalam mesin maya, maklumat generik telah dipadamkan, jadi generik tidak menyokong beberapa ciri masa jalan Mari kita lihatnya.

Mari analisa generik Java dan kad bebas generik bersama-sama

Kajian yang disyorkan: "tutorial video java"

Generik bukan ciri masa jalan

Kami masih mempunyai Ia bercakap tentang Open JDK

Oleh kerana sokongan generik disokong oleh pengkompil, maklumat generik telah dipadamkan apabila bytecode dimuatkan ke dalam mesin maya, jadi generik tidak menyokong beberapa ciri masa jalan. Oleh itu, sedar bahawa beberapa kaedah penulisan tidak akan disusun, seperti yang baru.

Seperti yang ditunjukkan di bawah, kelas Plate ialah kelas dengan generik, seperti yang ditunjukkan di bawah,

new Plate(...)
new Plate<T>(...)
class Plate<T> {
    T item;
    public Plate(T t) {
        new T();//是错误的,因为T是一个不被虚拟机所识别的类型,最终会被编译器擦除转为Object类给到虚拟机
        item = t;
    }
    public void set(T t) {
        item = t;
    }
    public T get() {
        return item;
    }
}
Salin selepas log masuk

T generik tidak boleh baharu, kerana T tidak dikenali oleh maya jenis mesin.

Kad bebas generik

Terdapat tiga bentuk ungkapan pembolehubah generik menggunakan aksara kad bebas, iaitu:

  • c, jenis elemen dalam c adalah semua A atau subkelas A

  • <:C

  • : C c, jenis elemen dalam c tidak pasti

Jom lihat maksudnya secara khusus dan cara menggunakannya~

Kad bebas sempadan atas

Dalam bidang pengaturcaraan berorientasikan objek, kami percaya bahawa pangkalan kelas asas berada di bahagian atas. Dari perspektif pokok warisan, kelas Objek berada di bahagian atas.

Jadi kami panggil ungkapan ini

Pertama lihat contoh berikut.

RequestBodyAdvice dalam Sping Webmvc

public interface RequestBodyAdvice {
   /**
    * Invoked first to determine if this interceptor applies.
    * @param methodParameter the method parameter
    * @param targetType the target type, not necessarily the same as the method
    * parameter type, e.g. for {@code HttpEntity<String>}.
    * @param converterType the selected converter type
    * @return whether this interceptor should be invoked or not
    */
   boolean supports(MethodParameter methodParameter, Type targetType,
         Class<? extends HttpMessageConverter<?>> converterType);
   ...
}
Salin selepas log masuk

Dalam ping Webmvc, RequestBodyAdvice Digunakan untuk memproses badan permintaan http, dan sokongan digunakan untuk menentukan sama ada penukaran jenis parameter tertentu kepada permintaan HttpMessage disokong.

HttpMessageConverter ialah antara muka, seperti kelas JsonViewRequestBodyAdvice yang menyokong Badan dalam format Json Pelaksanaannya adalah seperti berikut:

@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
      Class<? extends HttpMessageConverter<?>> converterType) {
   return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) &&
         methodParameter.getParameterAnnotation(JsonView.class) != null);
}
Salin selepas log masuk

Gunakan AbstractJackson2HttpMessageConverter untuk memproses JsonView daripada perpustakaan Jackson2 perpustakaan parsing Java JSON yang popular, ia juga HttpMessageConverter yang disertakan dengan Springboot.

Pengguna yang berbeza boleh menentukan jenis Nasihat yang berbeza, supaya ia boleh menyokong banyak jenis parameter seperti xml, jadi fungsi sping-webmvc akan menjadi lebih fleksibel dan serba boleh , banyak Jenis boleh diterjemahkan ke dalam permintaan HttpInputMessage yang berbeza melalui HttpMessageConverters yang berbeza. Seperti yang ditunjukkan di bawah,

@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
      Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
   for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
      if (advice.supports(parameter, targetType, converterType)) {
         request = advice.beforeBodyRead(request, parameter, targetType, converterType);
      }
   }
   return request;
}
Salin selepas log masuk

mendapatkan senarai nasihat padanan melalui getMatchingAdvice(parameter, RequestBodyAdvice.class), merentasi senarai ini dan menghuraikan nasihat yang menyokong parameter untuk mendapatkan permintaan jenis HttpInputMessage.

Ungkapan kad bebas sempadan atas tidak lagi boleh ditetapkan

Ungkapan yang menggunakan kad bebas sebelumnya tidak lagi boleh menetapkan medan generik. Malah, ini bermakna kad bebas sempadan atas tidak lagi ditetapkan Kad bebas yang ditetapkan Mari kita lihat demo ini.

    @Test
    void genericTest() {
       
        Plate<Apple> p = new Plate<Apple>(new Apple());
        p.set(new Apple());//可以set
          Apple apple = p.get();
          
        Plate<? extends Fruit> q = new Plate<Apple>(new Apple());
       
        Fruit fruit = q.get();
      
         q.set(new Fruit());//将编译错误
    }
Salin selepas log masuk

PlateUngkapan ini bermakna pengkompil Java hanya mengetahui bahawa Fruit dan kelas terbitannya disimpan dalam bekas tidak diketahui, ia mungkin Fruit, Apple Or subkelas lain. Selepas pengkompil memberikan p, plat tidak ditandakan sebagai "Apple", tetapi ditandakan dengan pemegang tempat "CAP#1" (yang boleh menjadi serius dengan menyahkompilasi kod bait dengan javap) untuk menunjukkan penangkapan Buah atau subkelas Buah.

Tetapi tidak kira sama ada ia ditulis sebagai kad bebas atau tidak, generik akhirnya merujuk kepada jenis tertentu dan pengkompil menggunakan "CAP#1" khas, jadi kami tidak boleh menetapkan semula medan ini lagi. ralat penyusunan dengan jenis yang tidak konsisten akan berlaku.

Tetapi ciri ini tidak menghalang penggunaan Rangka kerja menggunakan paradigma kad bebas sempadan atas untuk mencapai pengembangan fleksibel.

Kad bebas sempadan bawah

Seterusnya mari lihat kad bebas sempadan bawah, mewakili sebarang jenis kelas induk T atau T, dan jenis sempadan bawah ialah T.

Perangkap Bahasa

Kita mudah jatuh ke dalam perangkap dalam pemahaman, memikirkan bahawa hanya kelas asas Buah atau Buah boleh ditetapkan. Malah, hanya subkelas Buah dan Buah boleh ditetapkan. Mari tulis ujian unit untuk melihat.

@Test
void genericSuperTest() {
    Plate<? super Fruit> p = new Plate<Fruit>(new Fruit());
    p.set(new Apple()); //ok,存取的时候可以存任意可以转为T的类或T
    p.set(new Object()); //not ok,无法 set Object
    Object object = p.get();//ok
    Fruit object = p.get();//not ok,super Fruit不是Fruit的子类
}
Salin selepas log masuk

Apabila mengakses, anda boleh menyimpan kelas atau T yang boleh ditukar kepada T, iaitu kelas yang boleh menetapkan subkelas Buah atau Buah.

Tetapi anda mesti menggunakan objek untuk merujuknya apabila menggunakannya.

panggilan balik tak segerak spring-kafka

Sekarang, mari kita lihat contoh praktikal.

SettableListenableFuture是spring 并发框架的一个类,继承自Future,我们知道Future表示异步执行的结果,T表示返回结果的类型。ListenableFuture可以支持设置回调函数,如果成功了怎么处理,如果异常又如何处理。

在spring-kafka包里使用了SettableListenableFuture来设置异步回调的结果,kafka客户端调用 doSend发送消息到kafka队列之后,我们可以异步的判断是否发送成功。

public class SettableListenableFuture<T> implements ListenableFuture<T> {
  ...
   @Override
   public void addCallback(ListenableFutureCallback<? super T> callback) {
      this.settableTask.addCallback(callback);
   }
   @Override
   public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
      this.settableTask.addCallback(successCallback, failureCallback);
   }
 ...
Salin selepas log masuk

SettableListenableFuture有重载的addCallback函数,支持添加ListenableFutureCallback callback和SuccessCallback successCallback;当调用的异步方法成功结束的时候使用notifySuccess来触发onSuccess的执行,这个时候将实际异步执行的结果变成参数给callback调用。

private void notifySuccess(SuccessCallback<? super T> callback) {
   try {
      callback.onSuccess((T) this.result);
   }
   catch (Throwable ex) {
      // Ignore
   }
}
Salin selepas log masuk

SuccessCallback是一个函数式接口,从设计模式的角度来看是一个消费者,消费类型的result。ListenableFutureCallback同理。

public interface SuccessCallback<T> {
   /**
    * Called when the {@link ListenableFuture} completes with success.
    * <p>Note that Exceptions raised by this method are ignored.
    * @param result the result
    */
   void onSuccess(@Nullable T result);
}
Salin selepas log masuk

为什么要用notifySuccess(SuccessCallback callback)呢?

这是因为super能支持的范围更多,虽然实际产生了某一个具体类型的结果,比如kafka的send函数产生的结果类型为SendResult,其他的客户端可能使用其他的Result类型,但是不管是什么类型,我们在使用Spring的时候,可以对异步的结果统一使用Object来处理。

比如下面的这段代码,虽然是针对kafka客户端的。但对于其他的使用了Spring SettableListenableFuture的客户端,我们也可以在addCallback函数里使用Object来统一处理异常。

 @SneakyThrows
    public int kafkaSendAndCallback(IMessage message) {
        String msg = new ObjectMapper().writeValueAsString(message);
        log.debug("msg is {}. ", msg);
        ListenableFuture send = kafkaTemplate.send("test", msg);
        addCallback(message, send);
        return 0;
    }
    private void addCallback(IMessage msg, ListenableFuture<SendResult<String, String>> listenableFuture) {
        listenableFuture.addCallback(
                new SuccessCallback<Object>() {
                    @Override
                    public void onSuccess(Object o) {
                        log.info("success send object = " + msg.getContentType() + msg.getId());
                    }
                },
                new FailureCallback() {
                    @Override
                    public void onFailure(Throwable throwable) {
                        log.error("{}发送到kafka异常", msg.getContentType() + msg.getId(), throwable.getCause());
                    }
                });
    }
}
Salin selepas log masuk

声明某个条件的任意类型?

比如 Collection类的这个函数,

@Override
public boolean removeAll(Collection<?> collection) {
  return delegate().removeAll(collection);
}
Salin selepas log masuk

Collection的removeAll函数移除原集合中的一些元素,因为最终使用equals函数比较要移除的元素是否在集合内,所以这个元素的类型并不在意。

我们再看一个例子,LoggerFactory

public class LoggerFactory {
    public static Logger getLogger(Class<?> clazz) {
        return new Logger(clazz.getName());
    }
}
Salin selepas log masuk

LoggerFactory可以为任意Class根据它的名字生成一个实例。

总结:设计模式PECS

PECS是producer extends consumer super的缩写。

也是对我们上面的分析的一个总结

意思是extends用于生产者模式,而super用于消费者模式。

  • 消费者模式:比如上面的callback结果是为了消费;这些结果被消费处理。

  • 生产者模式:比如那些Converter,我们要处理特定格式的http请求,需要生产不同的转换器Converter。

推荐学习:《java视频教程

Atas ialah kandungan terperinci Mari analisa generik Java dan kad bebas generik bersama-sama. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:juejin.im
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!