Подтвердить что ты не робот

Можно ли использовать общий тип общего метода 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)

Но хотя я могу жить с этим, это не то, что я хотел.

Есть ли способ использовать общий тип, чтобы заставить тип всех аргументов метода?

4b9b3361

Ответ 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);
}