java 转换为 Comparable,然后比较

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1241281/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:46:43  来源:igfitidea点击:

Casting to a Comparable, then Comparing

javagenerics

提问by Monkey Boson

I'm really trying to like generics, but so far the trouble they've caused outweighs any benefits. Please, please show me I'm wrong.

我真的很喜欢泛型,但到目前为止,它们造成的麻烦超过了任何好处。拜托,请告诉我我错了。

I understand the necessity of adding @SuppressWarnings("unchecked") when using generic-free frameworks (Spring, Hibernate). This alone really reduces generics' value, as does requiring classes be passed into the constructor to avoid the pitfalls of erasure. However, the real thorn always seems to be casting. I usually try for a while to get the syntax right, but then give up my attempt at purity, add a @SuppressWarnings, and move on with my life.

我理解在使用无泛型框架(Spring、Hibernate)时添加 @SuppressWarnings("unchecked") 的必要性。仅这一点就确实降低了泛型的价值,因为需要将类传递到构造函数中以避免擦除的陷阱。然而,真正的刺似乎总是在铸造。我通常会尝试一段时间来使语法正确,但随后放弃了对纯度的尝试,添加了 @SuppressWarnings,然后继续我的生活。

Here's an example: I'm reflecting over a bean to look for differences between two instances. Some properties implement Comparable such that (a.equals(b) == false) but (a.compareTo(b) == 0) (e.g. BigDecimal, Date). In these cases, I want the property to be considered the same.

这是一个示例:我正在对 bean 进行反射以查找两个实例之间的差异。一些属性实现 Comparable 使得 (a.equals(b) == false) 但 (a.compareTo(b) == 0)(例如 BigDecimal、Date)。在这些情况下,我希望该属性被视为相同。

MyObject original = getOriginal();
MyObject updated = getUpdated();
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) {
    // Assume I'm putting in the try/catch block
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null);
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null);

    boolean isPropertySame;

    if (Comparable.class.isAssignableFrom(pOriginal.getClass())) {
        // Type safety: The method compareTo(Object) belongs to the raw type Comparable. References to generic type Comparable<T> should be parameterized
        isPropertySame = Comparable.class.cast(pOriginal).compareTo(Comparable.class.cast(pUpdated)) == 0;

        // The method compareTo(capture#19-of ?) in the type Comparable<capture#19-of ?> is not applicable for the arguments (capture#21-of ? extends Comparable)
        Comparable<?> comparable = Comparable.class.cast(pOriginal);
        isPropertySame  = comparable.compareTo(comparable.getClass().getTypeParameters()[0].getGenericDeclaration().cast(pUpdated)) == 0;

        // Even if I get the generics right, I still get an error if pOriginal is java.sql.Timestamp and pUpdated is java.util.Date (happens all the time with Hibernate).
        isPropertySame = (help);

    } else {
        isPropertySame = pOriginal.equals(pUpdated);
    }

    if (!isPropertySame) {
        PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated);
        dao.save(delta);
    }
}

Any ideas on what I could put into (help)?

关于我可以放入(帮助)什么的任何想法?

采纳答案by Monkey Boson

Well, given that I was not able to find a "pure" way to do this, and the fact that I kept running into corner cases (like, in addition to this one, the difficulty of handling properties that are collections), I decided to make my delta-generating method waydumber. I realized that I'm only testing 9 different types of objects, so I can just test which of the 9 objects I'm comparing, then cast to that object and do object-specific testing.

好吧,鉴于我无法找到一种“纯粹”的方式来做到这一点,而且我一直遇到极端情况(比如,除了这个,处理集合属性的困难),我决定让我的三角形生成方法方式笨。我意识到我只测试了 9 种不同类型的对象,所以我可以只测试我正在比较的 9 个对象中的哪一个,然后转换为该对象并进行特定于对象的测试。

Implementing in this way took about an hour, and even though I have to re-compile every time any of the objects change, I figure I'm still in the black even if I spend days on this maintenance.

以这种方式实现大约需要一个小时,即使每次有任何对象更改时我都必须重新编译,但我认为即使我花了几天时间进行维护,我仍然一无所获。

So, in the end, I guess the answer is that there is no answer. Java generics are implemented in such a way that it is impossible to avoid occasionally suppressing compiler warnings and risk run-time class cast exceptions.

所以,最后,我想答案是没有答案。Java 泛型的实现方式不可避免地会偶尔抑制编译器警告并冒运行时类转换异常的风险。

回答by Steve B.

This looks to me like going about it the hard way. You can either have your beans implement comparable, in which case you just compare them directly, or you create a comparator -

在我看来,这似乎是一种艰难的方式。您可以让您的 bean 实现可比性,在这种情况下,您只需直接比较它们,或者您创建一个比较器 -

public class Bean implements Comparable<Bean> {...}

   public int compareTo(Bean other){ ... }
}

or

或者

public int compare(Bean a, Bean b){ 
  Comparator<Bean> c = new Comparator<Bean>(){ 
    public int compareTo(Bean a, Bean b){ ... }
    public boolean equals(Object o){.. }
 };
   return c.compare(a, b);
}

I agree with you that java generics can get a bit, er... convoluted.

我同意你的看法,Java 泛型可能有点,呃......令人费解。

回答by notnoop

I don't quite see what's wrong with just simply doing the following:

我不太明白只是简单地执行以下操作有什么问题:

MyObject original = getOriginal();
MyObject updated = getUpdated();
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) {
    // Assume I'm putting in the try/catch block
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null);
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null);

    boolean isPropertySame;
    if (pOriginal instanceof Comparable) {
        @SuppressWarnings("unchecked")
        Comparable<Object> originalValue = (Comparable<Object>) pOriginal;
        @SuppressWarnings("unchecked")
        Comparable<Object> updatedValue = (Comparable<Object>) pUpdated;
        isPropertySame = originalValue.compareTo(updatedValue) == 0;
    } else {
        isPropertySame = pOriginal.equals(pUpdated);
    }

    if (!isPropertySame) {
        PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated);
        dao.save(delta);
    }
}

Using Class.castin your case really don't help at all with type safety.

Class.cast在你的情况下使用真的对类型安全没有帮助。

回答by erickson

It looks like the assumption is that if a class implements Comparable, the type parameter is the class itself. That is, "class X implements Comparable<X>". If that's the case, then it makes sense to say,

看起来假设是如果一个类实现了Comparable,类型参数就是类本身。即“ class X implements Comparable<X>”。如果是这样,那么说是有道理的,

X a = new X(1), b = new X(2);
a.compareTo(b);

However, it is definitely possible to define a class like "class X implements Comparable<Y>". Then one could attempt something like this…

但是,绝对可以定义像“ class X implements Comparable<Y>”这样的类。然后可以尝试这样的事情......

X a = new X(1), b = new X(2);
a.compareTo((Y) b);

… but clearly, a ClassCastExceptionwould be raised because bis not an instance of Y.

……但很明显, aClassCastException会被提出,因为b它不是 的实例Y

So, the warning is valid. The code, using raw types, is not type-safe, and could raise exceptions at runtime.

所以,警告是有效的。使用原始类型的代码不是类型安全的,并且可能在运行时引发异常。