Могу ли я пометить метод как устаревший для вызывающих абонентов kotlin, но не для вызывающих абонентов java?

Я хочу расширить API таким образом, чтобы это было полезно для вызывающих абонентов Kotlin, но не для вызывающих абонентов Java. Поэтому я хотел бы призвать пользователей Kotlin перейти на новый метод, не беспокоя пользователей Java. У нас есть большая кодовая база, которая представляет собой смесь Java и Kotlin, и в ближайшее время не будет 100% Kotlin.

Есть ли способ добавить тег @Deprecate или аналогичный, который отображается только в Kotlin, а не в Java?

Если вас интересуют конкретные детали, у нас есть интерфейс Log, который принимает форматированную строку, примерно так:

Log.get().info("Log message with some $expensive $things to $format")

Эта строка создается при каждом вызове, даже если ведение журнала отключено. Исправление API логгера, чтобы он принимал строку формата и аргументы отдельно, было бы большим и разрушительным изменением. Однако типы Kotlin, допускающие значение NULL, могут дать нам очень дешевое решение:

Log.getNullable()?.info("This $expensive $format should be skipped if there's no logger")

Как раз случай замены get(). на getNullable()?. во всем коде Kotlin. Но я бы не хотел использовать getNullable() в коде Java, потому что есть риск NullPointerException. Таким образом, в идеале старый метод Log.get должен быть устаревшим в Kotlin, но не в Java.

что возвращает Log.get() и как это могло быть нулевым? Вы делаете его допускающим значение NULL только для предотвращения чего-либо в вызывающем классе? (это кажется немного странным)

Tim 31.10.2018 12:41
Log.get() возвращает объект Logger (но обратите внимание, что это не стандартный java.util.logging.Logger по устаревшим причинам). Он всегда возвращает действительный Logger, но в некоторых конфигурациях сборки сообщения журнала просто отбрасываются, поэтому время, потраченное на форматирование строки, тратится. В основном это довольно дешево, но не всегда. Итак, да, я хочу вернуть null, чтобы вызывающий абонент не форматировал строку.
Iain Merrick 31.10.2018 12:48

не могли бы вы показать, как реализован текущий Log? или что это (объект? товарищ?)? также: как теперь определяются getNullable?

Roland 31.10.2018 13:10
2
3
766
2

Ответы 2

Предполагая класс Log, методы которого определены в companion object, например:

class Log {
  companion object {
    @JvmStatic
    fun get() : String = TODO()
    @JvmStatic
    fun getNullable() : String? = TODO()
  }
}

А как насчет того, чтобы просто удалить @JvmStatic? или вместо этого создать функцию расширения?

fun Log.Companion.getNullable() : String? = TODO()
class Log {
  companion object {
    @JvmStatic
    fun get() : String = TODO()
  }
}

Функции расширения обычно не так легко доступны в Java, как в Kotlin. И то, и другое, удаление @JvmStatic или функции расширения потребует в коде Java следующего: Log.Companion.getNullable вместо Log.getNullable ... Может быть, это тоже нормально для вас, вместо того, чтобы исключать его.

Даже если я ошибаюсь, функция расширения может удовлетворить ваши потребности.

Что касается устаревания метода только для Kotlin ... Я не думаю, что это сработает, но вот еще один способ обхода этого. Просто переименуйте метод и используйте @JvmName("get") для метода, например:

@JvmStatic
@JvmName("get")
fun dont_ever_use_this_method_again_in_Kotlin() : String = TODO()

Это позволит использовать Log.get с Java, но не с Kotlin. И я думаю, мы можем согласиться с тем, что никто не будет называть Log.dont_ever_use_this_method_again_in_Kotlin в Котлине, верно?

достаточно забавно ... просто поигрался, исключив объект-компаньон ... это не дало мне никаких ошибок в Java, но показало устаревание на стороне Kotlin :-)

Roland 31.10.2018 13:25

Да, я определенно хочу скрыть новый метод от вызывающих Java, и это хороший способ сделать это. Но мой вопрос в том, как отказаться от / скрыть старый метод для абонентов Kotlin.

Iain Merrick 31.10.2018 13:26

Вау, объект-компаньон устарел! Отличная идея, я попробую!

Iain Merrick 31.10.2018 13:27

для меня это скорее пахнет ошибкой ... при просмотре javadoc отображается устаревание ...: - /

Roland 31.10.2018 13:30

хорошо ... у вас есть кое-что, чтобы вы больше никогда не вызывали метод в Kotlin ;-) @JvmName может помочь ... (это просто обходной путь ... но это getNullable()?, верно? ;-))

Roland 31.10.2018 13:33

Хорошо, отказ от сопутствующего объекта меня частично утомляет, потому что устаревание проявляется в Kotlin, но не в Java. Однако он фактически осуждает все статических методов в Log, и я не вижу способа добавить новый, не устаревший (методы расширения также отображаются как устаревшие).

Iain Merrick 31.10.2018 13:34

Я думаю, что это, вероятно, ошибка ... по крайней мере, я ожидал, что Companion будет устаревшим и в Java ...

Roland 31.10.2018 13:35

Для этого есть две части

  • Скрыть метод от Kotlin Caller
  • Скрыть метод от Java Caller

Для сокрытия метода от Java Caller можно использовать @JvmSynthetic; подробнее об этом здесь. Чтобы скрыть метод от Kotlin Caller, я не смог найти прямой способ сделать это, но, как было предложено здесь, вы можете использовать @JvmName, чтобы указать хорошее имя для метода и добавить сообщение об устаревании в фактическое имя fun.

Итак, теперь класс будет выглядеть как

class Log {
    companion object {
        @JvmStatic
        @JvmName("get")
        fun dont_dare_using_this_from_kotlin_use_getNullable_instead(): String = TODO()

        @JvmSynthetic
        fun getNullable(): String = TODO()
    }
}

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