両方の型の変数と比較したいT extends Number
。今、私は2つの変数のうちどちらが他方より大きいか等しいかを知りたいです。残念ながら、私はまだ正確な型を知りません、私はそれがのサブタイプになることを知っているだけですjava.lang.Number
。どうやってやるの?
編集:私は別の回避策を試してみましたTreeSet
sは、実際には自然な順序で動作しました(もちろんそれは動作します。Number
実装するComparable
AtomicIntegerとAtomicLongを除く)。したがって、重複した値を失うことになります。使用するときList
s、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
(これは標準のJavaNumber
クラスにはありますが、Number
契約は要求しません)。
編集、7年後:コメントで指摘されているように、(少なくとも)3つの特別な場合があります。toString
あなたが考慮する必要があることを生み出すことができます:
Infinity
それが等しいということ以外は、それはすべてのものよりも大きい-Infinity
それが等しいということ以外は、すべてよりも小さいNaN
これは非常に毛深いです/それ以来比較することは不可能ですとのすべての比較NaN
結果false
それ自身との同等性をチェックすることを含む。Number
s。ここで最も良いことは、おそらくtypeパラメータをさらに制約していることでしょう。T extends Number & Comparable<T>
。 - gustafctoString
解析可能な値を返すBigDecimal
(これは標準のJavaNumber
クラスは"を行いますそれを考えると、それは完全に真実ではありません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()
ほとんどの場合はこれで十分なはずですが、ここでは、数値をdoubleに変換するときの微妙な問題があります。たとえば、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%正確な方法はありません。数値インタフェースには、exactValue()のように、情報を失うことなく完全な方法で数値を表すことができる何らかの型に変換するメソッドがありません。
実際にそのような完全な数を持つことは一般的に不可能です - 例えば、数Piを表すことは有限空間を使ったどんな算術を使っても不可能です。
if(yourNumber instanceof Double) {
boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
// [...]
}
注意:のinstanceof
チェックは必ずしも必要ではありません - それらをどれだけ正確に比較したいかによって異なります。もちろん、いつも使うことができます.doubleValue()
すべてのNumberはリストされたメソッドを提供するはずですここに。
編集する:コメントに述べられているように、あなたは(常に)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、あるいは浮動小数点数と倍精度数の組み合わせを渡すことができると仮定した場合、これらすべてのタイプのパラメーターを比較するための共通メソッドを1つ作成することはできません。
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
sを実装する
編集:
これは反省のためにコストがかかります。
編集2:
これはもちろん、あなたが小数点以下の桁数を整数またはいくつかのそのようなものと比較したい場合にはかかりません。
編集3:
これは、Comparableを実装していないNumberのカスタム定義の子孫がないことを前提としています(ありがとう@DJClayworth)。
Number
ですorg.apache.commons.math.fraction.BigFraction
これは私が自分のプログラムの多くで使用しています。 - finnw