(String) ou .toString ()?

89

Eu tenho um método com um Object oparâmetro.

Nesse método, eu sei exatamente que existe um Stringem "o" que não é nulo. Não há necessidade de verificar ou fazer outra coisa. Tenho que tratá-lo exatamente como um Stringobjeto.

Só por curiosidade - o que é mais barato - lance Stringou use Object.toString()? Ou é o mesmo por tempo- / cpu- / mem- preço?

Update: O método aceita Objectporque é a implementação de uma interface. Não há como alterar o tipo de parâmetro.

E não pode ser de nulltodo. Só queria dizer que não preciso verificar se há nulo ou vazio. No meu caso, sempre há uma string não vazia.

Vugluskr
fonte
1
No mundo .NET, nós medimos e ToString () é mais rápido. Dada a razão disso, o mesmo é quase certo com relação a um JVM jitting.
Josué

Respostas:

73

converter para uma String é mais barato, pois não requer uma chamada de função externa, apenas verificação de tipo interna.

euforia83
fonte
4
Você o testou em vários JREs? Tenho visto resultados surpreendentes para exatamente essa situação no .NET. Realisticamente, duvido que o desempenho importe na vida real - mas o casting é melhor do ponto de vista de codificação defensiva.
Jon Skeet
A chamada do método deve ser incorporada. Usar genéricos para remover o elenco (explícito) seria o melhor.
Tom Hawtin - tackline
@Jon Skeet: Concordo que a diferença no desempenho não será muito. @Tom Hawtin: Como o tipo do objeto que será recebido não é conhecido no momento da compilação, não consigo ver como a chamada do método pode ser sequenciada. Você pode, por favor, esclarecer?
euforia83
@ euphoria83: Iniciado pelo compilador JIT, não por javac.
Michael Myers
Na verdade, não, o método não pode ser embutido. O tipo só é conhecido como objeto e a implementação real depende do tipo de tempo de execução. Qual é mais rápido ainda depende da implementação, mas até onde me lembro (na verdade, testei com o microbenchmark em um ponto), o casting parece ser mais rápido. Esta não é uma resposta óbvia embora: a verificação de tipo nem sempre é mais rápida. Para o tipo String, pode ser, pois é um objeto (não uma interface), e o último.
StaxMan
45

Eu usaria um gesso. Isso valida seu "conhecimento" de que é uma string. Se por algum motivo você acabar com um bug e alguém passar algo diferente de uma string, acho que seria melhor lançar uma exceção (o que um elenco fará) do que continuar a executar com dados defeituosos.

Jon Skeet
fonte
7

Se você sabe que o objeto o é uma string, eu diria apenas lançá-lo em uma string e aplicá-lo dessa forma. Chamar toString () em um objeto que você sabe com certeza é uma String pode apenas adicionar confusão.

Se Object o pode ser qualquer coisa diferente de String, você precisará chamar toString ().

Andy White
fonte
Esta é a resposta correta para mim. Por quê? Porque a conversão (string)Registry.GetValue...lança uma exceção para tentar lançar um objeto Int32, enquanto Registry.GetValue...ToString()funciona conforme o esperado.
gravidade de
3

Eu não ficaria muito preocupado com o desempenho, se essa operação fosse feita apenas alguns milhares de vezes por segundo - não há diferença tangível.

Eu, entretanto, estaria preocupado em "conhecer" a entrada. Você tem um método que aceita um Objecte deve tratá-lo como tal, ou seja, você não deve saber nada sobre o parâmetro, a não ser que ele adere à Objectinterface, que por acaso tem um toString()método. Nesse caso, eu sugiro fortemente usar esse método em vez de apenas assumir qualquer coisa.

OTOH, se a entrada é sempre quer Stringou null, basta alterar o método de aceitar Strings, e verificar explicitamente para nulls (o que você deve fazer de qualquer maneira sempre que lidar com não-primitivos ...)

Henrik Paul
fonte
Eu disse que minha pergunta não tem nenhum significado valioso :) Estou apenas curioso para saber o que é teoricamente mais barato. Mas obrigado mesmo assim
Vugluskr
O custo dependerá de quão eficiente a VM é em chamadas de método virtual vs verificação de tipo. Isso é específico da implementação.
Jon Skeet
2

Dado que o tipo de referência é um objeto e todos os objetos têm um toString (), basta chamar object.toString (). String.toString () apenas retorna isso.

  • toString () é menos código para digitar.
  • toString () é menos bytecode.
  • fundir é uma operação cara VS uma chamada polimórfica.
  • o elenco pode falhar.
  • Use String.valueOf (object) que apenas chama object.toString () se não for nulo.
mP.
fonte
1

Se o que você tem em "o" é uma String, então não há muita diferença (provavelmente a conversão é mais rápida, mas isso é uma coisa de implementação de VM / Biblioteca).

Se "o" pode não ser uma String, mas é suposto ser uma String, então a conversão é o que você deseja (mas você deve fazer o método tomar uma String em vez de um Objeto).

Se "o" puder ser de qualquer tipo, você terá que usar toString - mas certifique-se de verificar primeiro se há nulo.

void foo(final Object o)
{
    final String str;

    // without this you would get a class cast exception
    // be wary of using instanceof though - it is usually the wrong thing to do
    if(o instanceof String)
    {
        str = (String)o;
    }    
}

ou

void foo(final Object o)
{
    final String str;

    // if you are 100% sure that o is not null then you can get rid of the else
    if(o != null)
    {
        str = o.toString();
    }
}

Prefiro codificar o último como:

void foo(final Object o)
{
    final String str;

    if(o == null)
    {
        throw new IllegalArgumentException("o cannot be null");
    }

    str = o.toString();
}
TofuBeer
fonte
Os primeiros 2 trechos não serão realmente compilados (a finalvariável pode não ter sido inicializada). Você precisa de um elseque irá lançar uma exceção ou inicializar strpara algo.
Bruno Reis
1

Achei estranhamente que o elenco foi mais lento do que a pesquisa vtable sugerida pela chamada tostring.

Joshua
fonte
1

Não pode haver uma 'string nula em o'. Se o for nulo, não contém uma string nula, é apenas nulo. Basta verificar o nulo primeiro. Se você lançar ou chamar ToString () em null, você travará.

Ed S.
fonte
2
Casting null não irá travar. Ele nem mesmo lançará uma NullPointerException, apenas chamará null para o novo tipo. :)
Bombe