Pergunta

Esta questão já tem uma resposta aqui:

Alguém sabe por que java.lang.Number não implementa Comparable? Isso significa que você não pode classificar Numbers com Collections.sort que me parece um pouco estranho.

Post atualização discussão:

Obrigado por todas as respostas úteis. Acabei fazendo mais algumas pesquisas sobre este tópico .

A explicação mais simples para o porquê java.lang.Number não implementa Comparable está enraizada em preocupações mutabilidade.

Para um pouco de revisão, java.lang.Number é o super-tipo abstrato de AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long e Short. Nessa lista, AtomicInteger e AtomicLong para não implementar Comparable.

cavando ao redor, eu descobri que não é uma boa prática para implementar Comparable em tipos mutáveis ??porque os objetos podem mudar durante ou após a comparação tornando o resultado da comparação inútil. Ambos AtomicLong e AtomicInteger são mutáveis. Os designers de API teve a prudência de não ter Number implementar Comparable porque teria constrangido implementação de futuros subtipos. Na verdade, AtomicLong e AtomicInteger foram adicionados em Java 1.5 tempo depois java.lang.Number foi inicialmente implementada.

Além de mutabilidade, provavelmente há outras considerações aqui também. A implementação compareTo em Number teria que promover todos os valores numéricos para BigDecimal porque é capaz de acomodar todos os sub-tipos Number. A implicação de que a promoção em termos de matemática e desempenho é um pouco claro para mim, mas minha intuição descobre que solução kludgy.

Foi útil?

Solução

Vale a pena mencionar que a seguinte expressão:

new Long(10).equals(new Integer(10))

é sempre false, que tende a viagem todos em algum ponto ou outro. Portanto, não só você não pode comparar Numbers arbitrárias, mas você não pode mesmo determinar se eles são iguais ou não.

Além disso, com os tipos reais primitivos (float, double), determinando se dois valores são iguais é complicado e tem que ser feito dentro de uma margem de erro aceitável. Tente código como:

double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
  d2 += 0.1d;
}
System.out.println(d2 - d1);

e você vai ficar com uma pequena diferença.

Então, de volta para a questão de fazer Number Comparable. Como você implementá-lo? Usando algo como doubleValue() não faria isso de forma confiável. Lembre-se do Number subtipos são:

  • Byte;
  • Short;
  • Integer;
  • Long;
  • AtomicInteger;
  • AtomicLong;
  • Float;
  • Double;
  • BigInteger; e
  • BigDecimal.

Você poderia codificar um método compareTo() de confiança que não transformar-se em uma série de se instanceof declarações? casos Number só tem seis métodos disponíveis para eles:

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue(); e
  • doubleValue().

Então eu acho Sun tomou a decisão (razoável) que Numbers só foram Comparable a instâncias de si mesmos.

Outras dicas

Para a resposta, consulte Java bugparade bug 4.414.323 . Você também pode encontrar uma discussão de comp. lang.java.programmer

Para citar a resposta Sun ao relatório de erro de 2001:

Todos os "números" não são comparáveis; comparável assume uma ordenação total de números é possível. Isso não é nem verdadeiro dos números de ponto flutuante; NaN (Não um número) não é nem menos do que, maior do que, nem igual a qualquer valor de ponto flutuante, até mesmo a própria. {Float, double} .compare impor um total encomendar diferente da ordenação do de ponto flutuante "<" e "=" operadores. Além disso, como atualmente implementadas, as subclasses de Number só são comparáveis ??a outras instâncias da mesma classe. Ha outro casos, como números complexos, onde nenhum ordenação total padrão existe, embora se possa ser definido. No Resumindo, se deve ou não uma subclasse de Número é comparável deve ser deixada como uma decisão para que subclasse.

a fim de implementar comparáveis ??sobre o número, você teria que escrever um código para cada par subclasse. É mais fácil, em vez de apenas permitir que subclasses para implementar comparáveis.

Muito provavelmente porque seria bastante ineficiente para comparar números -. A única representação na qual cada número pode caber para permitir tal comparação seria BigDecimal

Em vez disso, subclasses não-atômicos de implementos número comparável em si.

queridos atômicas são mutáveis, por isso não pode implementar uma comparação atômica.

Você pode usar Transmorph para comparar números usando sua classe NumberComparator.

NumberComparator numberComparator = new NumberComparator();
assertTrue(numberComparator.compare(12, 24) < 0);
assertTrue(numberComparator.compare((byte) 12, (long) 24) < 0);
assertTrue(numberComparator.compare((byte) 12, 24.0) < 0);
assertTrue(numberComparator.compare(25.0, 24.0) > 0);
assertTrue(numberComparator.compare((double) 25.0, (float) 24.0) > 0);
assertTrue(numberComparator.compare(new BigDecimal(25.0), (float) 24.0) > 0);

Para tentar resolver o problema original (tipo uma lista de números), uma opção é declarar a lista de um tipo genérico estendendo número e implementar Comparable.

Algo como:

<N extends Number & Comparable<N>> void processNumbers(List<N> numbers) {
    System.out.println("Unsorted: " + numbers);
    Collections.sort(numbers);
    System.out.println("  Sorted: " + numbers);
    // ...
}

void processIntegers() {
    processNumbers(Arrays.asList(7, 2, 5));
}

void processDoubles() {
    processNumbers(Arrays.asList(7.1, 2.4, 5.2));
}

não há comparação stardard para números de diferentes tipos. No entanto, você pode escrever seu próprio Comparador e usá-lo para criar um TreeMap , TreeSet ou Collections.sort (List , Comparador) ou Arrays.sort (Number [], Comparador);

Escreva a sua própria Comparator

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class NumberComparator implements Comparator {
    @SuppressWarnings("unchecked")
    @Override
    public int compare(Number number1, Number number2) {
 if (((Object) number2).getClass().equals(((Object) number1).getClass())) {
     // both numbers are instances of the same type!
     if (number1 instanceof Comparable) {
  // and they implement the Comparable interface
  return ((Comparable) number1).compareTo(number2);
     }
 }
 // for all different Number types, let's check there double values
 if (number1.doubleValue() < number2.doubleValue())
     return -1;
 if (number1.doubleValue() > number2.doubleValue())
     return 1;
 return 0;
    }

    /**
     * DEMO: How to compare apples and oranges.
     */
    public static void main(String[] args) {
 ArrayList listToSort = new ArrayList();
 listToSort.add(new Long(10));
 listToSort.add(new Integer(1));
 listToSort.add(new Short((short) 14));
 listToSort.add(new Byte((byte) 10));
 listToSort.add(new Long(9));
 listToSort.add(new AtomicLong(2));
 listToSort.add(new Double(9.5));
 listToSort.add(new Double(9.0));
 listToSort.add(new Double(8.5));
 listToSort.add(new AtomicInteger(2));
 listToSort.add(new Long(11));
 listToSort.add(new Float(9));
 listToSort.add(new BigDecimal(3));
 listToSort.add(new BigInteger("12"));
 listToSort.add(new Long(8));
 System.out.println("unsorted: " + listToSort);
 Collections.sort(listToSort, new NumberComparator());
 System.out.println("sorted:   " + listToSort);
 System.out.print("Classes:  ");
 for (Number number : listToSort) {
     System.out.print(number.getClass().getSimpleName() + ", ");
 }
    }
}

por que isso teria sido má ideia? :

abstract class ImmutableNumber extends Number implements Comparable {
    // do NOT implement compareTo method; allowed because class is abstract
}
class Integer extends ImmutableNumber {
    // implement compareTo here
}
class Long extends ImmutableNumber {
    // implement compareTo here
}

Outra opção pode ter sido a de implementos número de classe declarar Comparável, omitir implementação compareTo, e implementá-lo em algumas classes, como Integer, enquanto lance UnsupportedException em outros, como AtomicInteger.

Meu palpite é que, por não implementar Comparable, que dão mais flexibilidade para implementar classes de implementá-lo ou não. Todos os números comuns (Integer, Long, Double, etc) não implementar Comparable. Você ainda pode chamar Collections.sort enquanto os próprios elementos implementar Comparable.

Olhando para a hierarquia de classes. Wrapper aulas como Long, Integer, etc, implementar comparáveis, ou seja, um Integer é comparável a um número inteiro, e uma longa é comparável a um tempo, mas você não pode misturá-los. Pelo menos com este genéricos paradigma. Que eu acho que responde a sua pergunta 'porquê'.

byte (primitiva) é um int (primitiva). Primitivos têm apenas um valor de cada vez.
Idioma regras de projeto permite isso.

int i = 255

// down cast primitive
(byte) i == -1

A Byte não é uma Integer. Byte é um Number e um Integer é um Number. objetos Number pode ter mais de um valor, ao mesmo tempo.

Integer iObject = new Integer(255);
System.out.println(iObject.intValue());   // 255
System.out.println(iObject.byteValue());  // -1

Se um Byte é um Integer e um Integer é um Number, qual o valor que você irá usar no método compareTo(Number number1, Number number2)?

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