Локальная запись с типом универсального метода

Почему невозможно создать локальную запись, содержащую поля, соответствующие универсальному типу метода?

Не рабочий пример:

public class Test {

   <T> void foo(T param) {
       record Container(T content) { }
       Container test = new Container(param);
   }
}

Я получаю сообщение об ошибке: на переменную нестатического типа T нельзя ссылаться из статического контекста.

Работает, но на мой взгляд ненужно:

public class Test {

   <T> void foo(T param) {
       record Container<S>(S content) { }
       Container<T> test = new Container<>(param);
   }
}

Ваша цель кажется странной, но вам следует включить и ошибку в задачу. Вы получаете non-static type variable T cannot be referenced from a static context

matt 02.05.2024 14:55

@matt Я получаю сообщение: «На T нельзя ссылаться из статического контекста». В комментариях к одному из ответов Sweeper отмечает, что создание локальной записи меняет только ее видимость, но в остальном она аналогична вложенной записи (которая, как я знаю, всегда статична). Это объясняет мне сообщение об ошибке и поведение. Мой пример — это лишь минимальный случай моего варианта использования, и я признаю, что он не имеет особого смысла.

Lukas Betz 02.05.2024 15:10

Вложенные записи неявно статичны, поэтому вы не можете использовать универсальное значение времени выполнения. интерфейсы имеют ту же проблему. Так что это может быть хороший вопрос, поскольку записи новые, и я не могу найти дубликат. Можете ли вы включить ошибку? Возможно, измените его на одновложенный класс.

matt 02.05.2024 15:18

Они немного обсуждают это в 8.1.1.4

matt 02.05.2024 15:26

Записи должны определяться вне методов. Они должны быть видны всему классу.

Mr. Polywhirl 02.05.2024 15:30

@matt Я сейчас включил в вопрос ошибку компилятора. Хочешь, чтобы я еще что-нибудь изменил? Если я вас правильно понимаю, вы хотите, чтобы это было легко найти для дальнейшего использования. Если это поможет, не стесняйтесь редактировать все, что захотите. Вы и Sweeper ответили на мой вопрос в комментариях.

Lukas Betz 02.05.2024 15:30

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

Elikill58 02.05.2024 15:35

Я думаю, что размещение обычного класса с определенной вложенной записью имеет те же проблемы. Это тоже довольно распространенное явление. Ваш пример очень странный, поскольку объем записи очень ограничен. например, урок Test<T>{ record TR(T t){}} провален, но Test<T>{ record TR<S>(S s){} все в порядке

matt 02.05.2024 16:18

Как говорили другие, записи неявно статичны, а это означает, что даже если класс записи находится внутри метода foo, он не знает о существовании метода foo (или о существовании экземпляра, метод foo которого был вызван). Следовательно, он не знает, что такое T.

VGR 02.05.2024 17:54

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

Lukas Betz 02.05.2024 18:39
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
10
101
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы создаете record в методе foo. Это создаст новый класс. Если вы используете тип T, это не удастся, поскольку этот тип может изменить каждый вызов метода.

Используя record Container<S>(S content) { }, вы создаете в методе новый статический тип. Используя тип T, вы используете нестатический тип. Аналогичная проблема может возникнуть с ключевым словом final. Иногда, если переменная не является окончательной, ее невозможно использовать. Вам нужно создать вторую переменную, которая будет окончательной. В вашем случае окончательным типом создания является S.

Чтобы иметь возможность использовать тип T, вы можете использовать тип S, который будет «сфотографировать» фактический тип T (что то же самое, что сделать переменную окончательной), например:

public class Test {

   <T> void foo(T param) {
       record Container<S extends T>(S content) { }
       Container<T> test = new Container<>(param);
   }
}

В этом случае тип S должен быть типом T или его подклассом. И даже изменив тип T, компилятору будет все равно, поскольку он уже выбрал неизменяемый тип и установил использование этой записи в качестве статического типа.

Извините, я до сих пор этого не понимаю. Я знаю, что тип T может измениться при каждом вызове метода. Но что, если цель этой локальной записи — всегда просто фиксировать этот изменяющийся тип. Я не понимаю, почему компилятор этого не позволяет.

Lukas Betz 02.05.2024 14:54

@LukasBetz Локальная запись ничем не отличается от записи, вложенной во включающий класс. Тот факт, что вы помещаете его в метод, меняет только то, где можно использовать его имя. Это работает с классами, потому что внутренние классы — это вещь, а не «внутренние записи».

Sweeper 02.05.2024 15:00

Я только что добавил несколько объяснений по поводу этого @LukasBetz. Я надеюсь, что это хорошо ответит, и вы это поймете, также, к сожалению, у меня нет ссылки на документацию Java, чтобы объяснить это лучше.

Elikill58 02.05.2024 15:00

@Sweeper Ах, это объясняет, я не знал, что это только изменило видимость, но больше ничего.

Lukas Betz 02.05.2024 15:03

Я думаю, вы упускаете суть. Записи неявно статичны. Я не думаю, что «расширяет T» что-то добавляет, потому что они создают версию с T в качестве общей. Хотя, скорее всего, я упускаю вашу точку зрения.

matt 02.05.2024 15:22

Используя extends T, создается статический тип S, для которого требуется динамический тип T. Я пробовал на своей IDE, все работает нормально

Elikill58 02.05.2024 15:24

Я не вижу, что это добавляет.

matt 02.05.2024 15:28

Надеюсь, я хорошо объясняю, я просто отредактировал свой ответ

Elikill58 02.05.2024 15:33

Проблема, которую я вижу в вашем ответе, заключается в том, что я могу сделать это с помощью класса. class Container{ final T a; Container(T a){this.a = a;} }

matt 02.05.2024 17:30
Ответ принят как подходящий

Рекорд — это не просто замена класса.

public class Recorded<T>{
    class Bc{
        T t; 
    }
    record Br(T t){};
}

Класс скомпилирует файл, но запись не удалась, потому что

| на переменную нестатического типа T нельзя ссылаться из статического контекста
| запись Br(T t){};

Это связано с тем, что записи не могут быть внутренними классами или они неявно статичны.

В случае OP запись ограничена по объему, но подчиняется тем же правилам. По сути, поскольку запись не является внутренним классом, вы не можете создать конкретный тип из универсального.

Вот соответствующий вопрос об интерфейсах

Спасибо, что нашли время ответить на это. Метод, с помощью которого я наткнулся на это, был не таким странным, как тот, который я привел выше. Я просто разбил это настолько подробно, насколько это возможно, чтобы уловить суть, не прибегая к излишеству. Я действительно хочу понять детали языка, который использую, поэтому и задал этот вопрос. Еще раз спасибо и хорошего дня.

Lukas Betz 02.05.2024 18:58

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