Можно ли использовать общий тип общего метода Java для принудительного применения типа аргументов?
Я хотел бы использовать общий тип, чтобы гарантировать, что аргументы метода имеют один и тот же тип, например:
public static <T> void x(T a, T b)
Я бы предположил, что два аргумента (a и b), которые передаются этому методу, всегда должны быть одного типа. Но, к моему удивлению, мне удалось передать аргументы любого типа (даже примитивы) методу x, как если бы T было стерто с Object, независимо от того, какие аргументы переданы.
Единственная работа, которую я нашел до сих пор, заключалась в том, чтобы использовать "extends" следующим образом:
public static <T, U extends T> void x(T a, U b)
Но хотя я могу жить с этим, это не то, что я хотел.
Есть ли способ использовать общий тип, чтобы заставить тип всех аргументов метода?
Ответы
Ответ 1
Если я правильно понял ваш вопрос, вы хотите:
x(10, "x");
сбой во время компиляции.
Теперь подумайте об этом:
Integer i = 10;
String s = "x";
Object o1 = i;
Object o2 = s;
x(o1, o2);
В этом случае оба объекта - один и тот же. Я не думаю, что есть какой-либо способ реально обеспечить то, что вы хотите - когда вы передаете свой аргумент Object, всегда можно вызвать его двумя разными типами без каких-либо предупреждений/ошибок.
Вы можете указать тип, который хотите использовать, используя его следующим образом:
ClassName.<Type>x(obj1, obj2);
И это единственный способ сделать это.
Ответ 2
Если я правильно понимаю, один из способов сделать это - явно указать тип T вместо того, чтобы позволить компилятору сделать его тип самым прямым суперклассом в случае передачи двух объектов разных типов в качестве аргументов. Возьмите что-то вроде этого, например:
public class Test {
public static void main(String[] args) {
Test.x(5.0, 5); // This works since type is inferred to be Number
Test.<Integer>x(5, 5); // This works since type is stated to be Integer
Test.<Integer>x(5.0, 5); // This doesn't; type is stated to be Integer and Double is passed in
}
public static <T> void x(T a, T b) {
}
}
Ответ 3
Почему это должно быть проблемой, во-первых, для меня это немного туманно. Я подозреваю, что вы вместо этого неправильно поняли способы использования системы типов.
Что мы можем сделать с <T> void x(T a, T b)
? Ну, не так много. Внутри тела x
, T
совпадает с Object
, поэтому мы могли бы делать что-то вроде call toString
на a
и b
для их печати.
В действительности нет практической причины a
, а b
должен иметь тот же тип. Просто, что у них есть общий тип, и этот тип Object
или его подтип. На самом деле нет четкой причины, по которой <T> void x(T a, T b)
действительно должен быть общим вообще.
- Тело метода не волнует, каковы фактические типы
a
и b
, потому что они все равно не могут их использовать.
- На сайт вызова все равно, каковы фактические типы
a
и b
, потому что x
- это метод void
, так что это черная дыра.
Более типично для метода иметь результат, например <T> List<T> Arrays.asList(T...)
:
// This will cause a compile error because
// the type inferred must be compatible
// with the return assignment.
List<Integer> r = Arrays.asList(1, 1.0);
Или оценка:
// We don't care what the actual types of
// a and b are, just that we can call bar()
// on them.
// Note: this method does not need to be generic.
<T extends Foo> void x(T a, T b) {
a.bar();
a.bar();
}
Или оценка, которая утверждает какое-то отношение:
// We don't care what the actual types of
// a and b are, just that we can compare
// them to each other.
<T extends Comparable<T>> T max(T a, T b) {
return (a.compareTo(b) < 0) ? b : a;
}
Ответ 4
Вы можете явно указать параметр type при вызове метода. Например:
<String>x("hello", "world");
Однако, если вы не явно указываете параметр типа и полагаетесь только на функцию вывода типа Java, я не думаю, что вы можете не только в Generics, но и в вообще.
Тип параметра метода не является конкретным типом, а скорее чем-то, что обозначает набор применимых типов (даже этот набор может содержать только один тип, в случае классов final
, например).
Например, этот метод:
public void x(Something a) { }
обозначает метод, который должен иметь тип из набора типов, которые совместимы с Something
(т.е. Something
и всеми его подтипами).
То же самое относится к Generics.
Ответ 5
Предположительно, вы не вызываете свой общий метод в общем виде, поэтому он рассматривался как вызов x(Object a, Object b)
. В этом примере:
public class Test {
static <T> void x(T a, T b) {
}
public static void main(String[] args) {
x(1, 2); // compiles
Test.<String>x(1, 2); // does not compile
Test.<String>x("a", "b"); // compiles
}
}
Первый вызов x не производится в общем виде, поэтому он компилируется. Второй вызов приравнивает T
к String
, поэтому он терпит неудачу, потому что 1
и 2
не Strings
. Третий вызов компилируется, потому что он правильно проходит в Strings
.
Ответ 6
Это сработало для меня
public static <T> void x(T a, T b, Class<T> cls) {
}
теперь это компилирует
public static void main(String[] args) throws Exception {
x(1, 2, Integer.class);
}
и это не
public static void main(String[] args) throws Exception {
x(1, "", Integer.class);
}