Ответ 1
Reimeus уже указывал, что то, что вы просите в своем редактировании, невозможно. Я просто хотел бы немного рассказать о том, почему.
Можно подумать, что вы можете использовать следующее:
public <T, U extends T & IDisposable> void mapThis(
Class<? extends MyClass<T>> key,
Class<? extends U> value
) { ... }
В самом деле, что мне пришло в голову, когда я впервые увидел этот пост. Но это фактически приводит к ошибке компилятора:
переменная типа может не сопровождаться другими ограничениями
Чтобы помочь мне объяснить, почему я хотел бы процитировать сообщение в блоге Oracle от Виктора Рудометова об этой ошибке:
Этот факт не всегда ясен, но это правда. Следующий код не следует компилировать:
interface I {}
class TestBounds <U, T extends U & I> {
}
Потому что JLS Chapter 4 Типы, значения и переменные Раздел 4.4. Переменные типа: " bound состоит из либо переменной типа, либо класса или типа интерфейса T, за которым последуют дополнительные типы интерфейсов я 1,..., я n. может использовать T extends U, T extends SomeClass и I, но не T расширяет U и I. Это правило применяется ко всем случаям, включая переменные типа и границы в методы и конструкторы.
Причины этого ограничения изучаются в тесно связанной записи: Почему я не могу использовать аргумент типа в параметре типа с несколькими ограничениями?
Подводя итог, было наложено ограничение, чтобы "исключить некоторые неловкие ситуации, возникающие" (JLS §4.9).
Какие неудобные ситуации? Ответ Криса Повирка описывает один:
[Причиной ограничения является] возможность указания нелегальных типов. В частности, дважды расширив общий интерфейс с разными параметрами. Я не могу придумать не надуманный пример, но:
/** Contains a Comparator<String> that also implements the given type T. */ class StringComparatorHolder<T, C extends T & Comparator<String>> { private final C comparator; // ... } void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }
Теперь
holder.comparator
являетсяComparator<Integer>
и aComparator<String>
.
Крис также указывает на Sun bug 4899305, который был ошибкой, оспаривающей это ограничение языка. Он был закрыт, так как Will not Fix со следующим комментарием:
Если переменной типа могут следовать переменные типа или (возможно параметризованных) интерфейсов, вероятно, будет более взаимно рекурсивных переменных типа, которые очень трудно обрабатывать. вещи уже сложны, когда оценка является просто параметризованным типом, например
<S,R extends Comparable<S>>
. Следовательно, границы не идут теперь измениться. Как javac, так и Eclipse соглашаются, чтоS&T
иS&Comparable<S>
являются незаконными.
Итак, вот причины этого ограничения. Обращаясь к типичным методам (что касается вашего вопроса), я хотел бы еще раз отметить, что вывод типа теоретически приводил бы к тому, что такие оценки будут бессмысленными.
Если мы пересмотрим параметры типа, указанные в гипотетической сигнатуре выше:
<T, U extends T & IDisposable>
Предполагая, что вызывающий объект явно не указывает T
и U
, это можно свести к следующему:
<T, U extends Object & IDisposable>
Или просто это (тонкая разница, но что другая тема):
<T, U extends IDisposable>
Это связано с тем, что T
не имеет границ, поэтому независимо от того, какой тип аргументов передается, T
может, по крайней мере, разрешить Object
, а затем может U
.
Вернемся назад и скажем, что T
ограничено:
<T extends Foo, U extends T & IDisposable>
Это можно уменьшить тем же способом (Foo
может быть классом или интерфейсом):
<T extends Foo, U extends Foo & IDisposable>
Основываясь на этом рассуждении, синтаксис, который вы пытаетесь достичь, бессмыслен, поскольку ограничивает вызывающего абонента более конкретными аргументами.
Приложение Pre-Java 8:
До Java 8 существует прецедент для того, что вы пытаетесь сделать. Из-за ограничения того, как компилятор описывает параметры типа универсального метода, мои рассуждения выходят из окна. Возьмите следующий общий метод:
class MyClass {
static <T> void foo(T t1, T t2) { }
}
Это обычная ошибка начинающего, пытаясь сделать метод, который принимает два параметра "того же типа". Конечно, это бессмысленно из-за того, как работает наследование:
MyClass.foo("asdf", 42); // legal
Здесь T
определяется как Object
- это соответствует предыдущим рассуждениям об упрощении параметров типа mapThis
. Вы должны вручную указать параметры типа для достижения требуемой проверки типа:
MyClass.<String>foo("asdf", 42); // compiler error
Тем не менее,, и здесь, когда ваш прецедент начинает входить, это другое дело с несколькими параметрами типа с шахматными границами:
class MyClass {
static <T, U extends T> void foo(T t, U u) { }
}
Теперь эти ошибки вызова:
MyClass.foo("asdf", 42); // compiler error
Столы повернуты - мы должны вручную расслабить параметры типа, чтобы их компилировать:
MyClass.<Object, Object>foo("asdf", 42); // legal
Это происходит из-за ограниченного способа, с помощью которого компилятор запрашивает параметры типа метода. По этой причине то, что вы хотели достичь, на самом деле имело бы приложение для ограничения аргументов вызывающего абонента.
Однако эта проблема, по-видимому, исправлена в Java 8, а MyClass.foo("asdf", 42)
теперь компилируется без каких-либо ошибок (благодаря Regent для указания этого).