Почему невозможно создать локальную запись, содержащую поля, соответствующие универсальному типу метода?
Не рабочий пример:
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);
}
}
@matt Я получаю сообщение: «На T нельзя ссылаться из статического контекста». В комментариях к одному из ответов Sweeper отмечает, что создание локальной записи меняет только ее видимость, но в остальном она аналогична вложенной записи (которая, как я знаю, всегда статична). Это объясняет мне сообщение об ошибке и поведение. Мой пример — это лишь минимальный случай моего варианта использования, и я признаю, что он не имеет особого смысла.
Вложенные записи неявно статичны, поэтому вы не можете использовать универсальное значение времени выполнения. интерфейсы имеют ту же проблему. Так что это может быть хороший вопрос, поскольку записи новые, и я не могу найти дубликат. Можете ли вы включить ошибку? Возможно, измените его на одновложенный класс.
Они немного обсуждают это в 8.1.1.4
Записи должны определяться вне методов. Они должны быть видны всему классу.
@matt Я сейчас включил в вопрос ошибку компилятора. Хочешь, чтобы я еще что-нибудь изменил? Если я вас правильно понимаю, вы хотите, чтобы это было легко найти для дальнейшего использования. Если это поможет, не стесняйтесь редактировать все, что захотите. Вы и Sweeper ответили на мой вопрос в комментариях.
Я думаю, что люди, голосующие за закрытие этого вопроса, не основаны на мнениях, поскольку спрашивают, почему появляется эта ошибка, и пытаются понять.
Я думаю, что размещение обычного класса с определенной вложенной записью имеет те же проблемы. Это тоже довольно распространенное явление. Ваш пример очень странный, поскольку объем записи очень ограничен. например, урок Test<T>{ record TR(T t){}}
провален, но Test<T>{ record TR<S>(S s){}
все в порядке
Как говорили другие, записи неявно статичны, а это означает, что даже если класс записи находится внутри метода foo, он не знает о существовании метода foo (или о существовании экземпляра, метод foo которого был вызван). Следовательно, он не знает, что такое T
.
Не могли бы люди, проголосовавшие за закрытие этого вопроса, уточнить, на чем основано это мнение? Есть причина, по которой это не работает, и она не имеет ничего общего с мнением. Смотрите принятый ответ. Надеюсь, это не потому, что в вопросе было слово «мнение»..
Вы создаете 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 может измениться при каждом вызове метода. Но что, если цель этой локальной записи — всегда просто фиксировать этот изменяющийся тип. Я не понимаю, почему компилятор этого не позволяет.
@LukasBetz Локальная запись ничем не отличается от записи, вложенной во включающий класс. Тот факт, что вы помещаете его в метод, меняет только то, где можно использовать его имя. Это работает с классами, потому что внутренние классы — это вещь, а не «внутренние записи».
Я только что добавил несколько объяснений по поводу этого @LukasBetz. Я надеюсь, что это хорошо ответит, и вы это поймете, также, к сожалению, у меня нет ссылки на документацию Java, чтобы объяснить это лучше.
@Sweeper Ах, это объясняет, я не знал, что это только изменило видимость, но больше ничего.
Я думаю, вы упускаете суть. Записи неявно статичны. Я не думаю, что «расширяет T» что-то добавляет, потому что они создают версию с T в качестве общей. Хотя, скорее всего, я упускаю вашу точку зрения.
Используя extends T
, создается статический тип S
, для которого требуется динамический тип T
. Я пробовал на своей IDE, все работает нормально
Я не вижу, что это добавляет.
Надеюсь, я хорошо объясняю, я просто отредактировал свой ответ
Проблема, которую я вижу в вашем ответе, заключается в том, что я могу сделать это с помощью класса. class Container{ final T a; Container(T a){this.a = a;} }
Рекорд — это не просто замена класса.
public class Recorded<T>{
class Bc{
T t;
}
record Br(T t){};
}
Класс скомпилирует файл, но запись не удалась, потому что
| на переменную нестатического типа T нельзя ссылаться из статического контекста
| запись Br(T t){};
Это связано с тем, что записи не могут быть внутренними классами или они неявно статичны.
В случае OP запись ограничена по объему, но подчиняется тем же правилам. По сути, поскольку запись не является внутренним классом, вы не можете создать конкретный тип из универсального.
Вот соответствующий вопрос об интерфейсах
Спасибо, что нашли время ответить на это. Метод, с помощью которого я наткнулся на это, был не таким странным, как тот, который я привел выше. Я просто разбил это настолько подробно, насколько это возможно, чтобы уловить суть, не прибегая к излишеству. Я действительно хочу понять детали языка, который использую, поэтому и задал этот вопрос. Еще раз спасибо и хорошего дня.
Ваша цель кажется странной, но вам следует включить и ошибку в задачу. Вы получаете
non-static type variable T cannot be referenced from a static context