Вызовите константу объекта Java, используя переменную

Я очень новичок в Java, поэтому мне сложно объяснить, что я пытаюсь сделать.

У меня есть абстрактный класс, который вызывает несколько констант объекта, например:

public abstract class Enchantment implements Keyed {
    /**
     * Provides protection against environmental damage
     */
    public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper("protection");

В другом файле я могу получить к нему доступ с помощью
Enchantment value = Enchantment.PROTECTION_ENVIRONMENTAL;

Однако вместо этого я пытаюсь использовать строковую переменную. Что-то вроде этого:

String str = "PROTECTION_ENVIRONMENTAL";
Enchantment value = Enchantment.str;

Очевидно, это не сработает. Поэтому я провел кучу исследований и узнал, что для этого мне нужно использовать отражение. Используя документы этого исходного кода, я понял, что ищу полевые данные. Итак, я попробовал оба:

Field fld = Enchantment.class.getField("PROTECTION_ENVIRONMENTAL");
Field fld = Enchantment.class.getDeclaredField("PROTECTION_ENVIRONMENTAL");

Но они вернули мне исключение NoSuchFieldException. Пока я был на нем, я пробовал и getMethod(), и getDeclaredMethod() одинаково хорошо, но безуспешно.

Я сейчас в том, что это, вероятно, «объектные константы»? Я не знаю, как их назвать. Но я определенно не понимаю, как заставить это работать сейчас, и после всего, что я пробовал сам, я решил, что пришло время обратиться за помощью сюда.

и узнал, что мне нужно использовать отражение для этого. Нет нет Нет Нет. Вы не должны. Используйте Map<String, Enchantment>.
JB Nizet 15.06.2019 06:50
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
1
61
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете изучить перечисления, если знаете, что они будут постоянными значениями;

public enum Enchantment {
    PROTECTION_ENVIRONMENTAL {
        public void cast() {
            // do enum-specific stuff here
        }
    },
    ANOTHER_ENCHANTMENT {
        public void cast() {
            // do enum-specific stuff here
        }
    },
    A_THIRD_ENCHANTMENT{
        public void cast() {
            // do enum-specific stuff here
        }
    };

    public abstract void cast();
}

перечисления можно рассматривать как классы и иметь методы и свойства. Вы также можете конвертировать в строки и обратно Enchantment.valueOf("PROTECTION_ENVIRONMENTAL"), но обычно это происходит, если вы читаете из файла конфигурации — в коде вы бы напрямую ссылались на значение.

Это не сработает, потому что абстрактный класс также имеет методы и переменные, которые не являются постоянными значениями. Кажется, что когда я это делаю, они больше не работают (они становятся неопределенными).

icecub 15.06.2019 07:04

Перечисления Java довольно мощные, вы можете дать им методы, как и другим классам - я обновил ответ примером.

Dennis 15.06.2019 07:50

Спасибо, я обязательно еще над этим подумаю. На данный момент принятый ответ работает, и, поскольку я занимался этим всю ночь (сейчас для меня 8 утра), я сначала немного посплю, ха-ха

icecub 15.06.2019 07:58
Ответ принят как подходящий

Когда у вас есть Field, вам нужно вызвать Field.get(Object) с экземпляром (в данном случае class). Что-то типа,

Class<?> cls = Enchantment.class;
try {
    Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
    System.out.println(f.get(cls));
} catch (Exception e) {
    e.printStackTrace();
}

Поскольку вам нужен Enchantment, вы мог затем проверяете, что экземпляр, который вы получаете, может быть назначен Enchantment. Что-то типа,

Class<? extends Enchantment> cls = Enchantment.class;
try {
    Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
    Object obj = f.get(cls);
    if (cls.isAssignableFrom(obj.getClass())) {
        Enchantment e = cls.cast(obj);
        System.out.println(e);
    }
} catch (Exception e) {
    e.printStackTrace();
}

Но подход enum — это лучше.

Кажется, это близко, но как мне превратить f.get(cls) в Enchantment value = ? Когда я попробовал, он сказал мне, что не может преобразовать объект в чары. Я, наверное, действительно новичок здесь, ха-ха

icecub 15.06.2019 07:05

@icecub Видите, это настоящая проблема. Если это Поле В самом деле — Чары, то вам нужен только В ролях. Серьезно, честно: человеку, который не знает, почему и как В ролях объекты java, таким людям не хватает знаний и опыта, чтобы хотя бы приблизиться к рефлексии. Рефлексия — это то, чего даже опытные Java-программисты избегают как чумы. Это никогда не первый правильный вариант, это всегда последнее средство, когда у вас нет другого решения!

GhostCat 15.06.2019 07:28

@GhostCat Reflection полезно знать (но я согласен, что подход enum здесь лучше).

Elliott Frisch 15.06.2019 07:33

Извините, мне пришлось сначала провести тестирование, но ваш обновленный ответ работает отлично! Спасибо за терпение. Ты тоже @GhostCat

icecub 15.06.2019 07:56

Этот комментарий уместен: вы, абсолютно, не используете здесь отражение.

Есть только две веские причины для использования отражения:

  • вы создаете фреймворк, который должен иметь дело с классами, о которых он не знает
  • у вас есть какая-то другая причина иметь дело с классами, о которых вы не знаете во время компиляции

Но ваш код прекрасно знает об этом классе Enchantment, его возможностях и так далее. Поэтому отражение — неправильный подход. Вы сами поняли: чертовски трудно понять правильно, и чертовски правильно ошибиться в некоторых тонкостях. И когда вы ошибаетесь, он всегда взрывается во время выполнения. Код отражения составление ничего не значит. Он всегда ждет, пока вы запустите его, чтобы вырвать вам в лицо.

Итак, чтобы ответить на ваш вопрос, не отвечая на него: используйте карту. Нравиться:

Map<String, Enchantment> enchantmentsByConstantName = new HashMap<>();
enchantmentsByConstantName.put("PROTECTION_ENVIRONMENTAL", PROTECTION_ENVIRONMENTAL);

В качестве альтернативы эти константы могут быть включены в перечисление, как указано в другом ответе, но немного по-другому:

enum EnchantmentHolder {
  PROTECTION_ENVIRONMENTAL(new EnchantmentWrapper("protection")),
  ANOTHER_ENCHANTMENT(...)
  A_THIRD_ENCHANTMENT(...)
  ...;

  private Enchantment enchantment;

  private EnchantmentHolder(Enchantment enchantment) {
    this.entchantment = entchantment;
  }

  public Enchantment getEntchantment() { return entchantment; }

Да, но весь смысл попытки использовать переменную заключается в том, чтобы «преобразовать» значение переменной в имя класса. Если бы я использовал карту, мне нужно было бы объявить, какая строка вызывает какой класс. Я мог бы также просто назвать это напрямую? Возможно, я что-то упускаю из виду, если да, то мой плохой ^^

icecub 15.06.2019 07:23

@icecub Ничто в вашем вопросе не говорило о том, что у вас есть поля типов разные. И когда все о чарах, эта карта или перечисление работают! Дело в том, что Java — это статически компилируемый язык. Вы должны проектировать свое приложение таким образом, чтобы оно соответствовало этой парадигме, вместо того, чтобы пытаться превратить Java в какой-то «какой-то» динамический язык, где вы таким образом переходите от строк к коду. Кроме того, это действительно кричит о проблеме XY. Какую проблему действительный, по вашему мнению, можно решить, имея такие константы, и откуда возникает необходимость называть вещи «из строк»?

GhostCat 15.06.2019 07:26

Короче сложно объяснить. Я работаю над плагином для сервера Minecraft Bukkit. Сам сервер находится в бета-версии. Таким образом, используя код, предоставленный для получения всех чар, он возвращает только половину из них. Другая половина реализована, просто еще не в «цикле», чтобы вернуться. Я не могу изменить исходный код сервера, потому что плагин не будет работать ни для кого другого, если я это сделаю. Поэтому я должен обойти это и напрямую вызывать реализации из плагина. Я могу сделать это вручную, но с 500+ элементами это было бы безумием. Поэтому вместо этого я пытаюсь напрямую преобразовать пользовательскую команду (строку)

icecub 15.06.2019 07:31

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