2015-05-13 3 views
53

Я хотел бы использовать общий тип, чтобы гарантировать, что аргументы метода одного и того же типа, как это:Можно ли использовать общий тип общего метода Java для принудительного применения типа аргументов?

public static <T> void x(T a, T b) 

Я бы предположил, что два аргумента (а и б), то есть переданные этому методу, всегда должны быть одного типа. Но, к моему удивлению, мне удалось передать аргументы любого типа (даже примитивы) методу x, как если бы T было стерто с Object, независимо от того, какие аргументы переданы.

Единственная работа вокруг я нашел до сих пор, было использование «расширяет», как это:

public static <T, U extends T> void x(T a, U b) 

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

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

+7

Показать код. – GriffeyDog

+9

Какая у вас конечная цель? Если это то, что тип среды выполнения двух объектов одинаковый, вы не можете сделать это во время компиляции. Рассмотрим: «Номер n1 = Целое число.valueOf (1); Номер n2 = Double.valueOf (2.3); x (n1, n2) '. Но трудно дать больше указаний, чем это, не зная, чего вы в конечном итоге пытаетесь достичь. – yshavit

+2

Для чего это нужно? Почему это имеет значение? – Radiodef

ответ

20

Если я понимаю ваш вопрос corr ectly, вы хотите:

x(10, "x"); 

не работает во время компиляции. Теперь рассмотрим как это сделать:

Integer i = 10; 
String s = "x"; 
Object o1 = i; 
Object o2 = s; 
x(o1, o2); 

В этом случае они оба объекта - один и тот же тип. Я не думаю, что есть какой-либо способ навязать вам то, что вы хотите - когда вы передаете свой аргумент Object, всегда можно вызвать его двумя разными типами без каких-либо предупреждений/ошибок.

Вы можете указать тип, который вы хотите использовать, используя его, как это:

ClassName.<Type>x(obj1, obj2); 

И это proably единственный способ сделать это.

+1

+1 для понимания концепции простой концепции. Только один способ сделать два объекта (экземпляр) того же типа, указав, какой тип нам нужен x . Иначе, если мы попытаемся сказать два объекта (экземпляр) того же типа. Затем каждый объект имеет тип «Объект» – Panther

+0

Спасибо, это самые простые и ясные ответы всех и подтверждает мою гипотезу о том, что T всегда стирается до Object. Я как-то упустил возможность явно задать тип, когда я прочитал документы - спасибо за указание на это. – sys64738

+0

@ user2207767 Это не связано с стиранием, а скорее, что 'T' неограничен, поэтому аргументом для него может быть что угодно. – Radiodef

11

Вы можете явно указать параметр типа при вызове метода. Например:

<String>x("hello", "world"); 

Однако, если вы не указать тип-параметр явно и полагаться только на тип логического вывода особенность Java, то я не думаю, что можно не только в дженериков, но в основном.

тип параметра метода является не конкретный тип, но это скорее то, что обозначает набор применимых типов (даже этот набор может состоять из только одного типа, в случае final классов, для пример).

Например, этот метод:

public void x(Something a) { } 

обозначает способ, в котором параметр должен быть такого типа, из набора типов, которые совместимы с Something (т.е. Something и всех его подтипов).

То же самое относится к Generics.

7

Это работало для меня

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); 
} 
+1

Его все равно можно назвать «x (« abc », 123, Object.class);». – Radiodef

+2

@Radiodef, но в этом случае оба аргумента являются объектами –

+2

Да, но остается, что он не заставляет 'a' и' b' иметь один и тот же тип. Это все еще невозможно. Мы все еще можем передать все, что захотим. – Radiodef

29

Если я правильно понимаю, один из способов сделать это, чтобы explicitly specify the type of T вместо позволить компилятору вывести его тип как самый прямой суперкласс в случае двух объектов ts разных типов передаются в качестве аргументов. Возьмите что-нибудь подобное, например:

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) { 
    } 
} 
+0

Спасибо за ответ, я чуть не принял его как последний, но мне понравился Barteks2x чуть больше ... – sys64738

10

Предположительно, вы не называя свой общий метод в общем виде, так он рассматривается как призыв к 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 
    } 
} 

Первый вызов х не производится в общем случае, поэтому он компилируется. Второй звонок равен T - String, поэтому он не работает, потому что 1 и 2 не являются Strings. Третий вызов компилируется, потому что он правильно проходит в Strings.

14

Почему это должно быть проблемой, в первую очередь, для меня является туманной. Я подозреваю, что вы вместо этого неправильно поняли способы использования системы типов.

Что мы можем сделать с помощью <T> void x(T a, T b)? Ну, не так много. Внутри корпуса x, T - это то же самое, что и Object, поэтому мы могли сделать только что-то вроде звонка 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; 
}