LINUX.ORG.RU

Почему компилятор на это ругается?

Очевидно ValueContainer.class не кастится к Class<ValueContainer<CONTAINER, String>>

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 1)

Я хз что ты хотел но возможно подойдёт:

abstract class A<CONTAINER> {
    abstract public Class<? extends ValueContainer<CONTAINER, String>> getModelType();
}

class B extends ValueContainer<Integer, String> {}

class C extends A<Integer>{
    public Class<? extends ValueContainer<Integer, String>> getModelType() {
        return B.class;
    }
}

Возможно не скопилится - не проверял, но надеюсь идея ясна.

ya-betmen ★★★★★
()

Потому что информации о дженериках нет и нормальная ко(нтра)вариантность йаве чужда. Возвращай Class<? extends ...>.

cdshines ★★★★★
()
Ответ на: комментарий от ya-betmen

Очевидно ValueContainer.class не кастится к Class<ValueContainer<CONTAINER, String>>

Но что мешает прикастить? В рантайме же генерики не проверяются.

	ValueContainer<Collections, String> c1 = new ValueContainer<>(null, null);
	ValueContainer<Integer, String> c2 = new ValueContainer<>(null, null);
	assertEquals(c1.getClass(), c2.getClass());
	assertTrue(c1.getClass() == c2.getClass());

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от ya-betmen

Да, так будет работать, но странно что только так и никак иначе, и нельзя getModelType сразу определить в A.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от cdshines

Потому что информации о дженериках нет и нормальная ко(нтра)вариантность йаве чужда. Возвращай Class<? extends ...>.

В том то и дело, не могу, там @Override и компилятор так тоже не разрешает.

orm-i-auga ★★★★★
() автор топика

Есть такая штука, как type erasure - если коротко, то рантайм понятия не имеет о том, что же записано в дженериках, они проверяются и определяются только на этапе компиляции.
Сохранение информации о дженериках есть в виде одного из JEP-ов.

GblGbl ★★★★★
()
@Override
	public Class<ValueContainer<CONTAINER, String>> getModelType() {
		return (Class)ValueContainer.class;
	}

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

Deleted
()
Ответ на: комментарий от orm-i-auga

Да, так будет работать, но странно что только так и никак иначе, и нельзя getModelType сразу определить в A.

Ты хочешь странного, дженерики нужны что б проверять типы на этапе компиляции. Попробую пояснить логику, твой getModelType, возвращает класс, если ты создашь его инстанс то этот инстанс должен будет оперировать (а иначе нафиг дженерик) с типами CONTAINER и String. Но объект от голого ValueContainer будет оперировать с Object и Object.

Теоретически ситуацию помог бы решить возврат ValueContainer<CONTAINER, String>.class, но этого очевидно джава не умеет. Должна ли? Выше подсказали трюк с кастом к Class.

Если у тебя дженерик-параметры возвращаемого класса важны, то ты хочешь странную вещь, если же эти параметры не важны, то сделай просто public Class<ValueContainer> getModelType() и не майся.

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 2)

Потому что псевдополе ValueContainer.class имеет тип Class<ValueContainer>, который не может кастоваться к типу Class<ValueContainer<CONTAINER, String>>.

К Type Erasure и прочему это не имеет никакого отношения.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 3)
Ответ на: комментарий от orm-i-auga

Но что мешает прикастить?

Система типов.

В рантайме же генерики не проверяются.

А при компиляции проверяются. С таким аргументом можно смело выбрасывать все генерики из Java, они же не проверяются в рантайме.

Legioner ★★★★★
()
Ответ на: комментарий от ya-betmen

Попробую пояснить логику, твой getModelType, возвращает класс, если ты создашь его инстанс то этот инстанс должен будет оперировать (а иначе нафиг дженерик) с типами CONTAINER и String. Но объект от голого ValueContainer будет оперировать с Object и Object.

Но ведь в данном случае public Class<ValueContainer<CONTAINER, String>> getModelType() гарантирует проверку типов на этапе компиляции, которыми будет оперировать потребитель метода. А так как newInstance независимо от типов дженериков будет возвращать объект одного и того же класса, то запрет на возврат просто .class мне и был непонятен. Особенно то, что нельзя его прикастить к Class<ValueContainer<CONTAINER, String>>, но можно к просто Class.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Но что мешает прикастить?

Система типов.

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

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

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

В твоём конкретном случае странно выглядит сигнатура метода. Зачем тебе возвращать именно такой класс? Чем не устроил Class<ValueContainer>?

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Не устроил тем, что это абстрактный класс имплементирующий интерфейс с генериками, и я хочу в конечной реализации оперировать конкретным типом CONTAINER.

@SuppressWarnings("serial")
public abstract class StringValueContainerToVisibleStringConverter<CONTAINER> implements Converter<String, ValueContainer<CONTAINER, String>> {

	abstract protected String filter(CONTAINER container, String value);

	@Override
	public ValueContainer<CONTAINER, String> convertToModel(String value, Class<? extends ValueContainer<CONTAINER, String>> targetType, Locale locale)
			throws com.vaadin.data.util.converter.Converter.ConversionException {
		throw new UnsupportedOperationException();
	}

	@Override
	public String convertToPresentation(ValueContainer<CONTAINER, String> value, Class<? extends String> targetType, Locale locale)
			throws com.vaadin.data.util.converter.Converter.ConversionException {
		return filter(value.getContainer(), value.getValue());
	}

	@SuppressWarnings({
			"unchecked",
			"rawtypes"
	})
	@Override
	public Class<ValueContainer<CONTAINER, String>> getModelType() {
		return (Class) ValueContainer.class;
	}

	@Override
	public Class<String> getPresentationType() {
		return String.class;
	}

}

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

Как ты будешь использовать результат метода getModelType() ? Для вызова newInstance или для чего? Что будет, если ты будешь просто возвращать Class<ValueContainer>?

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Legioner

Это vaadin будет использовать метод, для проверки типов перед конвертированием. Если здесь частично отказываться от генериков (для ValueContainer), то нужны будут касты в convertToPresentation, что тоже не очень хорошо.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

Понятно. Хорошего решения в этом случае я не знаю, только как subwoofer написал. Можно вынести в отдельный метод, чтобы warning-и не плодить:

    @SuppressWarnings("unchecked")
    public static <T> Class<T> genericClass(Class<?> c) {
        return (Class<T>) c;
    }
Legioner ★★★★★
()
Ответ на: комментарий от Deleted

Каким боком в случае топикстартера можно его привернуть? Он нужен, когда нам в рантайме нужны значения генерика, т.е. когда нам надо избавиться от Type Erasure. А в случае топикстартера надо компилятор уговорить компилировать валидный с точки зрения логики код.

Legioner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.