나는 두 변수와 비교하기를 원한다.T extends Number
. 이제 저는 두 변수 중 어느 것이 다른 변수보다 더 큰지 알고 싶습니다. 불행히도 정확한 유형을 아직 알지 못하지만 하위 유형이 될 것임을 알고 있습니다.java.lang.Number
. 어떻게해야합니까?
편집하다: 다른 해결 방법을 사용해 보았습니다.TreeSet
실제로는 자연 순서와 함께 작동합니다 (물론 작동합니다, 모든 하위 클래스의Number
도구Comparable
AtomicInteger 및 AtomicLong 제외). 따라서 나는 중복 된 가치를 잃을 것이다. 사용할 때List
에스,Collection.sort()
바인딩 된 불일치로 인해 내 목록을 허용하지 않습니다. 매우 만족스럽지 않습니다.
작동하는 (그러나 부서지기 쉬운) 솔루션은 다음과 같습니다.
class NumberComparator implements Comparator<Number> {
public int compare(Number a, Number b){
return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
}
}
그래도별로 좋지는 않습니다.toString
에 의해 파싱 가능한 값을 반환BigDecimal
(표준 자바Number
수업은하지만,Number
계약은 요구하지 않는다).
편집, 7 년 후 :코멘트에서 지적한 바와 같이, (최소한?) 세 가지 특수한 경우가 있습니다toString
다음을 고려해야합니다.
Infinity
, 그것은 동등한 것을 제외하고는 모든 것보다 더 크다.-Infinity
, 그것은 동등한 것을 제외하고 모든 것보다 적다.NaN
, 그것은 매우 털이 많다 / 비교할 수 없기 때문에모든 비교와NaN
의 결과false
, 평등 자체를 검사하는 것을 포함하여.Number
에스. 여기서 가장 좋은 것은 아마도 유형 매개 변수를T extends Number & Comparable<T>
. - gustafctoString
에 의해 파싱 가능한 값을 반환BigDecimal
(표준 자바Number
수업은 "그것은 전적으로 사실이 아닙니다.Double
과Float
3 개의 특수 값을 반환 할 수 있습니다 (NaN
,Infinity
, 및-Infinity
) 그것은아니~에 의해 지원되는BigDecimal
. - Andreas
이것은 Number를 확장하고 자신과 비교할 수있는 모든 클래스에서 작동합니다. & 비교하면 Sarmun 응답과 비교할 때 모든 유형 검사를 제거 할 수 있으며 런타임 유형 검사와 오류 던져를 무료로 제공합니다.
class NumberComparator<T extends Number & Comparable> implements Comparator<T> {
public int compare( T a, T b ) throws ClassCastException {
return a.compareTo( b );
}
}
요청한 후비슷한 질문여기에 대한 답을 공부하면서 다음과 같은 것을 생각해 냈습니다. 나는 gustafc가 제공 한 솔루션보다 더 효율적이고 강력하다고 생각합니다.
public int compare(Number x, Number y) {
if(isSpecial(x) || isSpecial(y))
return Double.compare(x.doubleValue(), y.doubleValue());
else
return toBigDecimal(x).compareTo(toBigDecimal(y));
}
private static boolean isSpecial(Number x) {
boolean specialDouble = x instanceof Double
&& (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
boolean specialFloat = x instanceof Float
&& (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
return specialDouble || specialFloat;
}
private static BigDecimal toBigDecimal(Number number) {
if(number instanceof BigDecimal)
return (BigDecimal) number;
if(number instanceof BigInteger)
return new BigDecimal((BigInteger) number);
if(number instanceof Byte || number instanceof Short
|| number instanceof Integer || number instanceof Long)
return new BigDecimal(number.longValue());
if(number instanceof Float || number instanceof Double)
return new BigDecimal(number.doubleValue());
try {
return new BigDecimal(number.toString());
} catch(final NumberFormatException e) {
throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
}
}
Double.toString(double)
방법을 사용한 다음BigDecimal(String)
생성자. " 정확한 전환으로 종종 예기치 않은 결과가 나오는 경우가 있습니다 (예 :0.1
리터럴). 그러나 숫자에 대해서는 아무 것도 알지 못하기 때문에 ' 기원, 나는 그들을 다루는 유일한 합리적인 방법은 그것에 대해 아무 것도 생각하지 않는 것이라고 생각한다. - rolve
당신을 위해 일할 수있는 한 가지 해결책은T extends Number
하지만 함께T extends Number & Comparable
. 이 유형은 다음을 의미합니다. "T
구현하는 유형에만 설정할 수 있습니다.양자 모두인터페이스. "
따라서 모든 비슷한 숫자로 작동하는 코드를 작성할 수 있습니다. 정적으로 타이핑되고 우아한.
이것은 BennyBoy가 제안하는 것과 같은 솔루션이지만 비교기 클래스뿐만 아니라 모든 종류의 메서드와 함께 작동합니다.
public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}
public void test() {
compfunc(2, 1); // Works with Integer.
compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
compfunc(2, 1.0); // Compilation error! Different types.
compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}
이것은 Number를 확장하고 자신과 비교할 수있는 모든 클래스에서 작동합니다.
class NumberComparator<T extends Number> implements Comparator<T> {
public int compare(T a, T b){
if (a instanceof Comparable)
if (a.getClass().equals(b.getClass()))
return ((Comparable<T>)a).compareTo(b);
throw new UnsupportedOperationException();
}
}
가장 "일반적인"Java 원시 번호는 double이므로 간단히 사용하십시오.
a.doubleValue() > b.doubleValue()
대부분의 경우 충분하지만, 숫자를 두 배로 변환 할 때 미묘한 문제가 있습니다. 예를 들어 BigInteger에서는 다음과 같이 할 수 있습니다.
BigInteger a = new BigInteger("9999999999999992");
BigInteger b = new BigInteger("9999999999999991");
System.out.println(a.doubleValue() > b.doubleValue());
System.out.println(a.doubleValue() == b.doubleValue());
결과 :
false
true
이것이 매우 극단적 인 경우라고 생각하지만 이것은 가능합니다. 아니요 - 일반적인 100 % 정확한 방법은 없습니다. Number 인터페이스는 exactValue ()와 같은 메소드가 없기 때문에 정보를 잃어 버리지 않고 숫자를 완벽하게 표현할 수있는 유형으로 변환됩니다.
실제로 완벽한 숫자를 갖는 것은 불가능합니다. 예를 들어 숫자 Pi를 나타내는 것은 유한 공간을 사용하는 산술을 사용하여 불가능합니다.
if(yourNumber instanceof Double) {
boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
// [...]
}
노트 :그만큼instanceof
확인이 반드시 필요한 것은 아닙니다. 정확히 어떻게 비교할 것인지에 달려 있습니다. 물론 당신은 물론 항상.doubleValue()
, 모든 번호는 나열된 방법을 제공해야하므로이리.
편집하다: 코멘트에 명시된 바와 같이 BigDecimal과 친구들을 확인해야합니다 (항상). 그러나 그들은.compareTo()
방법:
if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) {
boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
}
당신은 단순히 사용할 수 있습니다.Number's doubleValue()
그들을 비교하는 방법; 그러나 결과가 귀하의 요구에 충분히 정확하지 않을 수 있습니다.
이건 어때? 확실히 좋은 것은 아니지만 언급 된 모든 필요한 경우를 다룹니다.
public class SimpleNumberComparator implements Comparator<Number>
{
@Override
public int compare(Number o1, Number o2)
{
if(o1 instanceof Short && o2 instanceof Short)
{
return ((Short) o1).compareTo((Short) o2);
}
else if(o1 instanceof Long && o2 instanceof Long)
{
return ((Long) o1).compareTo((Long) o2);
}
else if(o1 instanceof Integer && o2 instanceof Integer)
{
return ((Integer) o1).compareTo((Integer) o2);
}
else if(o1 instanceof Float && o2 instanceof Float)
{
return ((Float) o1).compareTo((Float) o2);
}
else if(o1 instanceof Double && o2 instanceof Double)
{
return ((Double) o1).compareTo((Double) o2);
}
else if(o1 instanceof Byte && o2 instanceof Byte)
{
return ((Byte) o1).compareTo((Byte) o2);
}
else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
{
return ((BigInteger) o1).compareTo((BigInteger) o2);
}
else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
{
return ((BigDecimal) o1).compareTo((BigDecimal) o2);
}
else
{
throw new RuntimeException("Ooopps!");
}
}
}
다음과 같은 메소드가 있다고 가정 해 보겠습니다.
public <T extends Number> T max (T a, T b) {
...
//return maximum of a and b
}
정수 만있는 경우, long과 double을 매개 변수로 전달할 수 있습니다. 메소드 서명을 다음과 같이 변경할 수 있습니다.
public <T extends Number> T max(double a, double b) {
return (T)Math.max (a, b);
}
이것은 byte, short, integer, long 및 double에 대해 작동합니다.
BigInteger 또는 BigDecimal 또는 부동 소수점 및 복식 혼합이 전달 될 수 있다고 가정하면 이러한 모든 유형의 매개 변수를 비교하는 일반적인 방법을 만들 수 없습니다.
Number 인스턴스가못Atomic (즉 AtomicInteger) 다음과 같은 것을 할 수 있습니다 :
private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<? extends Number> n1Class = n1.getClass();
if (n1Class.isInstance(n2)) {
Method compareTo = n1Class.getMethod("compareTo", n1Class);
return (Integer) compareTo.invoke(n1, n2);
}
return -23;
}
이것은 모든 비 원자Number
Comparable를 구현합니다.
편집하다:
이것은 반영으로 인해 값이 비쌉니다.
2 번 수정:
이것은 물론 소수점을 int 또는 일부와 비교하려고하는 경우를 취하지 않습니다 ...
3 수정:
이것은 Comparable을 구현하지 않는 Number의 사용자 정의 하위 클래스가 없다고 가정합니다 (thanks @DJClayworth)
Number
~이다.org.apache.commons.math.fraction.BigFraction
이 프로그램은 많은 프로그램에서 사용합니다. - finnw