Genéricos Java T vs Object

127

Eu queria saber qual é a diferença entre as duas declarações de método a seguir:

public Object doSomething(Object obj) {....}

public <T> T doSomething(T t) {....}

Existe algo que você pode / faria com um, mas não com o outro? Não encontrei essa pergunta em nenhum outro lugar deste site.

Abidi
fonte

Respostas:

112

Isolado do contexto - sem diferença. Em ambos te objvocê pode chamar apenas os métodos de Object.

Mas com o contexto - se você tem uma classe genérica:

MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();

Então:

Foo newFoo = my.doSomething(foo);

Mesmo código com objeto

Foo newFoo = (Foo) my.doSomething(foo);

Duas vantagens:

  • não há necessidade de transmissão (o compilador esconde isso de você)
  • compile a segurança do tempo que funciona. Se a Objectversão for usada, você não terá certeza de que o método sempre retornará Foo. Se retornar Bar, você terá um ClassCastException, em tempo de execução.
Bozho
fonte
14

A diferença aqui é que, no primeiro, especificamos que o chamador deve passar uma instância de Object (qualquer classe) e receberá outro Object (qualquer classe, não necessariamente do mesmo tipo).

No segundo, o tipo retornado será o mesmo que o fornecido quando a classe foi definida.

Example ex = new Example<Integer>();

Aqui, especificamos qual será o tipo T, o que nos permite impor mais restrições a uma classe ou método. Por exemplo, podemos instanciar um LinkedList<Integer>ou LinkedList<Example>e sabemos que quando chamamos um desses métodos, retornamos uma instância de Integer ou Example.

O principal objetivo aqui é que o código de chamada possa especificar em que tipo de objetos uma classe operará, em vez de depender da conversão de tipos para impor isso.

Consulte Java Generics * da Oracle.

* Link atualizado.

Adão
fonte
13

A diferença é que, com métodos genéricos, não preciso converter e recebo um erro de compilação quando faço algo errado:

public class App {

    public static void main(String[] args) {

        String s = process("vv");
        String b = process(new Object()); // Compilation error
    }

    public static <T> T process(T val) {

        return val;
    }
}

Usando o objeto eu sempre preciso converter e não recebo erros quando faço algo errado:

public class App {

    public static void main(String[] args) {

        String s = (String)process("vv");
        String b = (String)process(new Object());
    }

    public static Object process(Object val) {

        return val;
    }
}
user1883212
fonte
assim como mencionar que você não tem que objetos elenco mais quer, a partir das android 6.
John Lord
2

Você não precisa fazer elenco de classe adicional. No primeiro caso, você sempre obterá um objeto da classe java.lang.Object, que precisará converter para sua classe. No segundo caso, T será substituído pela classe definida na assinatura genérica e nenhuma conversão de classe será necessária.

Andrey Adamovich
fonte
2

Em tempo de execução, nada. Mas, em tempo de compilação, o segundo fará a verificação de tipo para garantir que o tipo do parâmetro e o tipo do valor de retorno correspondam (ou sejam subtipos) ao tipo T resolvido (o primeiro exemplo também faz a verificação de tipo, mas todo objeto é um subtipo de objeto para que todos os tipos sejam aceitos).

Jonathan
fonte
2

T é um tipo genérico. Isso significa que ele pode ser substituído por qualquer objeto qualificado no tempo de execução. Você pode invocar esse método da seguinte maneira:

String response = doSomething("hello world");

OU

MyObject response = doSomething(new MyObject());

OU

Integer response = doSomething(31);

Como você pode ver, há polimorfismo aqui.

Mas se for declarado para retornar Object, você não poderá fazer isso a menos que digite as coisas de conversão.

adarshr
fonte
Podemos dizer que com <T>não há autoboxing?
SMUsamaShah
0

no primeiro caso, ele pega um parâmetro de qualquer tipo egstring e retorna um tipo foo. No segundo caso, ele pega um parâmetro do tipo foo e retorna um objeto do tipo foo.

fastcodejava
fonte