Pergunta

Esta não é uma questão do que é boxing e unboxing, é bastante por fazer linguagens como Java e C # necessidade isso?

Eu sou muito familiarizado C econômicos com ++, STL e Boost.

Em C ++ eu poderia escrever algo como isso muito facilmente,

std::vector<double> dummy;

Eu tenho alguma experiência com Java, mas eu fiquei muito surpreendido porque eu tinha que escrever algo como isto,

ArrayList<Double> dummy = new ArrayList<Double>();

A minha pergunta, por que deveria ser um objeto, o que é tão difícil tecnicamente para incluir tipos primitivos quando se fala de Genéricos?

Foi útil?

Solução

O que é tão difícil tecnicamente para incluir tipos primitivos quando se fala de Genéricos?

O caso de Em Java, é por causa da maneira genéricos trabalho. Em Java, os genéricos são um truque de tempo de compilação, que o impede de colocar um objeto Image em um ArrayList<String>. No entanto, os genéricos do Java são implementadas com o tipo de apagamento: a informação de tipo genérico é perdida durante o tempo de execução. Este foi por razões de compatibilidade, pois os genéricos foram adicionados bastante tarde na vida de Java. Isto significa que, em tempo de execução, um ArrayList<String> é efetivamente uma ArrayList<Object> (ou melhor: apenas ArrayList que espera e retornos Object em todos os seus métodos) que lança automaticamente para String quando você recupera um valor.

Mas desde int não deriva de Object, você não pode colocá-lo em um ArrayList que espera (em tempo de execução) Object e você pode não lançar um Object para int quer. Isto significa que o int primitivo tem de ser embrulhados em um tipo que faz herdam Object, como Integer.

C #, por exemplo, funciona de forma diferente. Genéricos em C # também são aplicadas em tempo de execução e não de boxe é necessário com um List<int>. Boxe em C # só acontece quando você tentar armazenar um tipo de valor como int em uma variável tipo de referência como object. Desde int em C # herda a partir Object em C #, escrevendo object obj = 2 é perfeitamente válido, no entanto, o int será encaixotado, que é feito automaticamente pelo compilador (nenhum tipo de referência Integer é exposto ao usuário ou qualquer coisa).

Outras dicas

Boxing e unboxing são uma necessidade nascida fora do caminho que linguagens (como C # e Java) implementar suas estratégias de alocação de memória.

Alguns tipos são alocados na pilha e outra na pilha. A fim de tratar um tipo alocado pilhas tal como um tipo alocado-montão, boxe é necessária para mover o tipo alocado-pilha para a pilha. Unboxing é os processos inversos.

Em C tipos # alocado-stack são chamados tipos de valor (por exemplo System.Int32 e System.DateTime) e tipos alocados-heap são chamados tipos de referência (por exemplo System.Stream e System.String).

Em alguns casos é vantajoso ser capaz de tratar um tipo de valor como um tipo de referência (reflexão é um exemplo), mas na maioria dos casos, boxing e unboxing são evitados melhor.

Eu acredito que este é também porque os primitivos não herdam de Object. Suponha que você tenha um método que quer ser capaz de aceitar qualquer coisa como o parâmetro, por exemplo.

class Printer {
    public void print(Object o) {
        ...
    }
}

Você pode precisar passar um valor primitivo simples para esse método, como:

printer.print(5);

Você seria capaz de fazer isso sem boxing / unboxing, porque 5 é um primitivo e não é um objeto. Você poderia sobrecarregar o método de impressão para cada tipo primitivo para habilitar essa funcionalidade, mas é uma dor.

Eu só posso dizer-lhe para Java porque ele não suporta tipos primitve em genéricos.

Primeiro, houve o problema que a questão para apoiar esta cada vez que trouxe a discussão se java deve mesmo ter tipos primitivos. Que, naturalmente, impediu a discussão da questão real.

Segundo a principal razão para não incluí-lo foi que eles queriam binário compatibilidade com versões anteriores para que ele seria executado sem modificações em uma VM não tem conhecimento de genéricos. Esta razão compatibilidade compatibilidade / migração para trás também é por isso que agora os genéricos suporta Collections API e permaneceu o mesmo e não há (como em C #, quando introduziu os genéricos) um novo conjunto completo de um genérico API Colecção consciente.

A compatibilidade foi feito usando (informações do parâmetro de tipo genérico removidos em tempo de compilação) ersure que é também a razão pela qual você obter tantos avisos elenco não verificada no java.

Você pode ainda adicionar os genéricos reificado, mas não é assim tão fácil. Basta adicionar o tipo de tempo de execução INFO Adicionar em vez de removê-lo não vai funcionar como quebra fonte e compatibilidade binária (você não pode continuar a usar os tipos de matérias-primas e você não pode chamar existente código compilado porque eles não têm os métodos correspondentes ).

A outra abordagem é o C # escolheu: ver acima

E autoboxing automatizado / unboxing foi não suportado para este caso de uso porque autoboxing custos muito.

Java teoria e prática: Genéricos gotchas

Em Java e C # (ao contrário do C ++) tudo estende Object, então classes de coleções como ArrayList pode conter objetos ou qualquer de seus descendentes (basicamente qualquer coisa).

Por motivos de desempenho, no entanto, primitivos em Java, ou tipos de valor em C #, foram dadas um estado especial. Eles não são objeto. Você não pode fazer algo como (em Java):

 7.toString()

Mesmo que toString é um método no objeto. De modo a colmatar esta aceno de desempenho, objectos equivalentes foram criados. Autoboxing remove o código clichê de ter de colocar um primitivo em sua classe de mensagens publicitárias e tirá-lo novamente, tornando o código mais legível.

A diferença entre os tipos de valor e objetos em C # é mais cinza. Consulte aqui sobre como eles são diferentes.

Cada cadeia não-objeto não-matriz armazenado na memória contém um 8- ou 16 bytes de cabeçalho (tamanhos para os sistemas de 32/64 bits), seguido pelo conteúdo de pública desse objecto e campos privados. Matrizes e cordas têm o cabeçalho acima, além de mais alguns bytes que definem o comprimento da matriz e tamanho de cada elemento (e possivelmente o número de dimensões, comprimento de cada dimensão extra, etc.), seguindo-se todos os campos do primeiro elemento, então todos os campos da segunda, etc. Dada uma referência a um objeto, o sistema pode facilmente examinar o cabeçalho e determinar que tipo é.

locais de referência do tipo de armazenamento conter um valor de quatro ou oito bytes que identifica exclusivamente um objeto armazenado na pilha. Em implementações atuais, esse valor é um ponteiro, mas é mais fácil (e semanticamente equivalente) para pensar nisso como uma "identificação de objeto".

locais de armazenamento

Valor do tipo armazenar o conteúdo de campos do tipo de valor, mas não têm qualquer cabeçalho associado. Se código declara uma variável do tipo Int32, não há necessidade de precisar armazenar informações com que Int32 dizer o que é. O fato de que esse local possui um Int32 é efetivamente armazenados como parte do programa, e por isso não tem que ser armazenado no próprio local. Este um representar uma grande economia se, por exemplo, tem um milhão de objetos cada um dos quais tem um campo de tipo Int32. Cada um dos objetos segurando o Int32 tem um cabeçalho que identifica a classe que pode operá-lo. Uma vez que uma cópia desse código de classe pode operar em qualquer dos milhões de casos, tendo o fato de que o campo é um Int32 ser parte do código é muito mais eficiente do que ter o armazenamento para cada um desses campos incluem informações sobre o que é .

O boxe é necessária quando é feita uma solicitação para passar o conteúdo de um local de armazenamento do tipo valor ao código que não sabe esperar que tipo de valor particular. Código que espera objetos de tipo desconhecido pode aceitar uma referência a um objeto armazenado na pilha. Uma vez que cada objeto armazenado na pilha tem um cabeçalho identificando o tipo de objeto que ele é, o código pode usar esse cabeçalho sempre que é necessário o uso de um objeto de uma forma que seria necessário saber o seu tipo.

Note que em .net, é possível declarar que são chamados de classes e métodos genéricos. Cada tal declaração gera automaticamente uma família de classes ou métodos que são idênticos, exceto forte que tipo de objeto sobre o qual eles esperam para agir. Se se passa um Int32 para um DoSomething<T>(T param) rotina, que vai gerar automaticamente uma versão da rotina em que cada instância do tipo T é efectivamente substituído com Int32. Essa versão da rotina vai saber que cada local de armazenamento declarado como tipo T detém uma Int32, assim como no caso em que uma rotina foi codificado para usar um local de armazenamento Int32, não será necessário para armazenar informações de tipo com os próprios locais.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top