Улучшенный переключатель для закрытых классов

Иногда сторонняя библиотека содержит API, которые возвращают закрытые классы, на которые вы не можете ссылаться напрямую. Одним из таких примеров является org.apache.avro.generic.GenericRecord.get(), который иногда может возвращать объект java.nio.HeapByteBuffer. Если бы я захотел переключить этот класс, я получу ошибку компиляции:

Object recordValue = genericRecord.get(someField);
switch (actualValue) {
   case String avroString -> {
      // do logic
   }
   case HeapByteBuffer avroBuffer -> {
      // do logic
   }
   default -> log.warn("Unknown type");
}

Если вместо этого я попытаюсь использовать расширяющий класс, код скомпилируется, но зарегистрирует предупреждающее сообщение «Неизвестный тип»:

Object recordValue = genericRecord.get(someField);
switch (actualValue) {
   case String avroString -> {
      // do logic
   }
   case ByteBuffer avroBuffer -> {
      // do logic
   }
   default -> log.warn("Unknown type");
}

Как я могу использовать расширенный переключатель для частного класса?

Класс HeapByteBuffer, похоже, создан на основе шаблона и даже не существует как исходный файл в исходном коде JDK. Вы не можете эффективно ссылаться ни на что внутри него, кроме как на ByteBuffer, и ваш код может сломаться при любом обновлении JDK. Что именно вы намерены делать там, где у вас есть // do logic необходимое HeapByteBuffer? Если все, что вам нужно, — это избежать предупреждения, какое предупреждение вы увидите?

Jim Garrison 29.05.2024 01:16

@ThomasKläger Я имел в виду, что вызывается строка log.warn(). Т.е. Дело обходит стороной ByteBuffer.

Joe Caruso 29.05.2024 02:08

@JimGarrison Я не понимаю, что вы имеете в виду, когда говорите, что HeapByteBuffer создается на основе шаблона. Это часть JDK, и она была такой уже долгое время. Сегодня в мире JDK21+ это даже явно разрешено в закрытой иерархии. Я не думаю, что обновление JDK избавит от этого... Кроме того, меня не волнует, что это HeapByteBuffer. Это именно то, что мне дает Avro. Я совершенно счастлив относиться к этому как к ByteBuffer, если это все, что у меня есть. Если у вас есть ответ о том, как использовать HeapByteBuffer или лучший способ решения этой проблемы, чем я уже публиковал, я буду рад его услышать!

Joe Caruso 29.05.2024 02:36

Ну, у меня есть Javadoc JDK20, и он не содержит HeapByteBuffer. Где вы видите Javadoc для этого класса?

Jim Garrison 29.05.2024 02:42

case ByteBuffer b по сути является просто проверкой instanceof. Итак, если вы используете java.nio.ByteBuffer, а объектом является java.nio.HeapByteBuffer, то будет выполнен этот случай, а не вариант по умолчанию. Если ваш код не работает таким образом, я предлагаю вам отредактировать свой вопрос, чтобы предоставить минимально воспроизводимый пример.

Slaw 29.05.2024 03:23

«Я не понимаю, что вы имеете в виду, когда говорите, что HeapByteBuffer создается на основе шаблона». Это просто означает, что большинство подтипов Buffer на самом деле не существуют в виде исходных файлов в репозитории OpenJDK. Они генерируются из *.template файлов (см. здесь) перед компиляцией. И все конкретные реализации являются частными для пакета. Но это не имеет отношения к вашему вопросу, кроме того, что java.nio.HeapByteBuffer не доступен напрямую и является деталью реализации.

Slaw 29.05.2024 03:29
HeapByteBuffer является подклассом ByteBuffer (и всегда было так, начиная с Java 1.4), и поэтому ваш оператор log никогда не должен выполняться. Возможно, ваша реальная проблема заключается в том, что, извлекая поле из avro с помощью Object recordValue = genericRecord.get(someField);, вы на самом деле обрабатываете какое-то другое значение actualValue с неясной связью с извлеченным значением.
Thomas Kläger 29.05.2024 06:38

Помимо исправления использования фактического значения вместо RecordValue, может помочь дополнительное ведение журналов, например log.warn("Unknown type "+actualValue+" cls = "+actualValue.getClass()).

DuncG 29.05.2024 09:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
8
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Второй пример в моем оригинальном ответе работает нормально. Не знаю, что я делал, чтобы нажать log.warn() раньше:

Object recordValue = genericRecord.get(someField);
switch (actualValue) {
   case String avroString -> {
      // do logic
   }
   case ByteBuffer avroBuffer -> {
      // do logic
   }
   default -> log.warn("Unknown type");
}

Вы не ответили на ключевой комментарий, а именно: какую ошибку вы получаете? Потому что ваш исходный код (например, код в вашем вопросе, а не в этом ответе) отлично работает для всех, кроме вас, очевидно. Какое предупреждение вы получите? Возможно, у вас есть инструмент проверки или IDE, который добавляет предупреждения, и вы настроили его так, чтобы он предупреждал о глупых вещах, или он сломан. Мы не сможем помочь, если вы не сообщите нам, какие дополнительные линтеры/компиляторы вы используете; включение точного текста предупреждения может означать, что кто-то знает, какой инструмент линтера вы неправильно настроили.

rzwitserloot 29.05.2024 00:40

@rzwitserloot Я отредактировал ответ, чтобы было более понятно, что предупреждение, о котором я имел в виду, было строкой log.warn(), которая вызывается, когда recordValue является HeapByteBuffer...

Joe Caruso 29.05.2024 02:10

Смотрите мой ответ. Вы этого не понимаете. Этот обходной путь бесполезен.

rzwitserloot 29.05.2024 02:45

Вы действуете в условиях серьезных недоразумений. Вот тривиальный пример, показывающий, что ваша мысленная модель происходящего здесь просто неверна:

class Test {
    public static void main(String[] args) {
        Object i = Integer.valueOf(42);
        switch (i) {
            case Number n -> System.out.println("N: " + n.intValue());
            default -> System.out.println("NaN");
        }
    }
}

Это печатает N: 42.

Другими словами, ваша теория о том, что вы должны назвать точный, точный тип, которым является эта вещь, просто неверна — вы можете вставить супертип в оператор case, и он отлично сработает. Ваш «обходной путь» написания case Object i when i instanceof ByteBuffer — это глупый способ писать case ByteBuffer b.

Если вы наблюдаете за срабатыванием кейса default, то либо [A] вы не компилируете/запускаете код, который, по вашему мнению, у вас есть, либо [B] HeapByteBuffer не является ByteBuffer, и ваш case Object i when i instanceof ByteBuffer тоже не будет работать, потому что это не так. t байтовый буфер.

Другие вопросы по теме