重构通用的compareTo方法

发布于 2024-10-29 09:17:29 字数 3856 浏览 4 评论 0 原文

我在做什么:

我有一个名为 Os 的容器类,它可以包含不同类型的元素以及类 Os 的实例。当我比较这个类时,我想看到:

  • 浅等于元素
  • 深等于 Os 元素

我已经确保,类中包含的每个元素:

  • 不能为空。
  • 与同类型元素相当。
  • 是一成不变的。嗯,至少我正在检查一部分。

以下是我目前所拥有的。

示例:

例如,这个测试用例会通过。

    Os o1 = Os.of(3, 4d, Os.of("-"));
    Os o2 = Os.of(Os.of(Character.toString('-')), 4.0, new Integer(3));

    assertEquals(o1.toString(), "[3, 4.0, [-]]");
    assertEquals(o2.toString(), "[[-], 4.0, 3]");
    assertTrue(o1.reverse().compareTo(o2) == 0);

代码示例:

compareTo 方法:

@Override
public int compareTo(final Os that) {
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;
    int subresult = 0;
    Comparable<?> othis;
    Comparable<?> othat;

    if (that == null)
        return AFTER;
    if (this == that)
        return EQUAL;

    subresult = ((Integer) this.o.size()).compareTo(that.o.size());
    if (subresult < 0)
        return BEFORE;
    else if (subresult > 0)
        return AFTER;

    try {
        for (int i = 0; i < this.o.size(); i++) {
            othis = this.o.get(i);
            othat = that.o.get(i);

            if (othis.getClass() == othat.getClass()) {
                if (othat instanceof Os) {
                    subresult = ((Os) othis).compareTo(((Os) othat));
                    if (subresult < 0)
                        return BEFORE;
                    else if (subresult > 0)
                        return AFTER;
                } else {
                    subresult = hackCMP(othis, othat);
                    if (subresult < 0)
                        return BEFORE;
                    else if (subresult > 0)
                        return AFTER;
                }
            } else {
                subresult = othis.getClass().getName()
                        .compareTo(othat.getClass().getName());
                if (subresult < 0)
                    return BEFORE;
                else if (subresult > 0)
                    return AFTER;
            }
        }
        return EQUAL;
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return BEFORE;
}

private static int hackCMP(Object val, Object val2)
        throws SecurityException, NoSuchMethodException,
        IllegalArgumentException, IllegalAccessException,
        InvocationTargetException {
    Method m = val.getClass().getMethod("compareTo", val.getClass());
    return (Integer) m.invoke(val, val2);
}

问题:

我想重构代码。

例如:

  • 如果可能的话,我宁愿不使用 hackCMP 方法。
  • 以下代码段似乎多次重复。我可以用什么东西代替它吗?

     子结果 = <表达式>;
            if(子结果<0)
                返回之前;
            否则如果(子结果> 0)
                之后返回;
            //别的 ...
    

我可以重构什么以及如何重构?

编辑:

@wolfcastle:数据存储在 private final ImmutableList> 中Ø;

我想提一下,每个答案都很有用。以下似乎有效:

@Override
public int compareTo(final Os that) {
    Ordering<Iterable<Comparable<?>>> order = //
    Ordering.natural().<Comparable<?>> lexicographical();
    int result = -1;

    try {
        result = ComparisonChain.start()
                .compare(this.o.size(), that.o.size())
                .compare(this.o, that.o, order).result();
    } catch (Exception e) { //ignore: type mismatch
    }

    return result;
}

What am I doing:

I have a container class named Os, that can contains different type elements and also instances of class Os. When I compare this class, I want to see :

  • shallow equals for elements
  • deep equals for Os elements

I have ensured, that every single element contained in class:

  • Can not be null.
  • Is comparable to same type elements.
  • Is immutable. Well, at least part that I'm checking.

Following is what I have at the moment.

Example:

For example, this test case will pass.

    Os o1 = Os.of(3, 4d, Os.of("-"));
    Os o2 = Os.of(Os.of(Character.toString('-')), 4.0, new Integer(3));

    assertEquals(o1.toString(), "[3, 4.0, [-]]");
    assertEquals(o2.toString(), "[[-], 4.0, 3]");
    assertTrue(o1.reverse().compareTo(o2) == 0);

Code example:

compareTo method:

@Override
public int compareTo(final Os that) {
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;
    int subresult = 0;
    Comparable<?> othis;
    Comparable<?> othat;

    if (that == null)
        return AFTER;
    if (this == that)
        return EQUAL;

    subresult = ((Integer) this.o.size()).compareTo(that.o.size());
    if (subresult < 0)
        return BEFORE;
    else if (subresult > 0)
        return AFTER;

    try {
        for (int i = 0; i < this.o.size(); i++) {
            othis = this.o.get(i);
            othat = that.o.get(i);

            if (othis.getClass() == othat.getClass()) {
                if (othat instanceof Os) {
                    subresult = ((Os) othis).compareTo(((Os) othat));
                    if (subresult < 0)
                        return BEFORE;
                    else if (subresult > 0)
                        return AFTER;
                } else {
                    subresult = hackCMP(othis, othat);
                    if (subresult < 0)
                        return BEFORE;
                    else if (subresult > 0)
                        return AFTER;
                }
            } else {
                subresult = othis.getClass().getName()
                        .compareTo(othat.getClass().getName());
                if (subresult < 0)
                    return BEFORE;
                else if (subresult > 0)
                    return AFTER;
            }
        }
        return EQUAL;
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return BEFORE;
}

private static int hackCMP(Object val, Object val2)
        throws SecurityException, NoSuchMethodException,
        IllegalArgumentException, IllegalAccessException,
        InvocationTargetException {
    Method m = val.getClass().getMethod("compareTo", val.getClass());
    return (Integer) m.invoke(val, val2);
}

Question:

I would like to refactor the code.

For example:

  • I would prefer not using hackCMP method, if possible.
  • Following code segment seems to repeat itself a lot. Can I replace it with something?

            subresult = <expression>;
            if (subresult < 0)
                return BEFORE;
            else if (subresult > 0)
                return AFTER;
            //else ...
    

What can I refactor and how to do it?

Edit:

@wolfcastle : Data is stored in private final ImmutableList<Comparable<?>> o;.

I'd like to mention, that every answer was useful. Following seems to work:

@Override
public int compareTo(final Os that) {
    Ordering<Iterable<Comparable<?>>> order = //
    Ordering.natural().<Comparable<?>> lexicographical();
    int result = -1;

    try {
        result = ComparisonChain.start()
                .compare(this.o.size(), that.o.size())
                .compare(this.o, that.o, order).result();
    } catch (Exception e) { //ignore: type mismatch
    }

    return result;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

淡写薰衣草的香 2024-11-05 09:17:29

我考虑的一种选择是将元素存储在一个类中,如果它们不是同一类,则允许按类而不是通过其 compareTo 方法比较它们:

private static class Element implements Comparable<Element> {
  // raw Comparable allows you to call compareTo
  private final Comparable comparable;

  Element(Comparable comparable) {
    this.comparable = comparable;
  }

  @Override @SuppressWarnings("unchecked")
  public int compareTo(Element o) {
    Comparable other = o.comparable;
    if(comparable.getClass().isInstance(other)) {
      return comparable.compareTo(other);
    }

    return comparable.getClass().getName().compareTo(other.getClass().getName());
  }

  @Override
  public boolean equals(Object obj) {
    return obj instanceof Element && comparable.equals(((Element) obj).comparable);
  }

  @Override
  public int hashCode() {
    return comparable.hashCode();
  }

  @Override
  public String toString() {
    return comparable.toString();
  }
}

然后,您的内部列表是一个 List,您在 Os 中的 compareTo 方法可能非常简单。使用 Guava,它可能非常简单:

@Override
public int compareTo(Os o) {
  return ComparisonChain.start()
       .compare(list.size(), o.list.size())
       .compare(list, o.list, Ordering.natural().<Element>lexicographical())
       .result();
}

One option I would consider would be storing the elements in a class that allows them to be compared by class rather than by their compareTo method if they aren't the same class:

private static class Element implements Comparable<Element> {
  // raw Comparable allows you to call compareTo
  private final Comparable comparable;

  Element(Comparable comparable) {
    this.comparable = comparable;
  }

  @Override @SuppressWarnings("unchecked")
  public int compareTo(Element o) {
    Comparable other = o.comparable;
    if(comparable.getClass().isInstance(other)) {
      return comparable.compareTo(other);
    }

    return comparable.getClass().getName().compareTo(other.getClass().getName());
  }

  @Override
  public boolean equals(Object obj) {
    return obj instanceof Element && comparable.equals(((Element) obj).comparable);
  }

  @Override
  public int hashCode() {
    return comparable.hashCode();
  }

  @Override
  public String toString() {
    return comparable.toString();
  }
}

Then, with your internal list being a List<Element>, your compareTo method in Os could be pretty simple. Using Guava, it could be extremely simple:

@Override
public int compareTo(Os o) {
  return ComparisonChain.start()
       .compare(list.size(), o.list.size())
       .compare(list, o.list, Ordering.natural().<Element>lexicographical())
       .result();
}
花想c 2024-11-05 09:17:29

您可以有一个返回 BEFORE | 的方法。之后 |不确定(比如说),然后调用它。

result = newMethod(subresult);
if (result != INDETERMINATE) return result;

这并没有多大的改进,仍然需要到处复制,只是更严格了一些。

You could have a method that returned BEFORE | AFTER | INDETERMINATE (say), then call it.

result = newMethod(subresult);
if (result != INDETERMINATE) return result;

That's not much of an improvement, and it still needs to be duplicated everywhere, but it's a little tighter.

轮廓§ 2024-11-05 09:17:29

由于 List> 的泛型类型是o 属性不固定,我会摆脱通用类型并依赖原始类型。它花费一个@SuppressWarnings("rawtypes"),但它减少了很多。

@Override
@SuppressWarnings("rawtypes")
public int compareTo(final Os that) {
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if (that == null)
        return AFTER;
    if (this == that)
        return EQUAL;

    int subresult = ((Integer) this.o.size()).compareTo(that.o.size());

    if (subresult != EQUAL)
        return subresult;

    for (int i = 0; i < this.o.size(); i++) {
        Comparable othis = this.o.get(i);
        Comparable othat = that.o.get(i);
        subresult = othis.compareTo(othat);

        if (subresult != EQUAL)
            return subresult;
    }

    return EQUAL;
}

Since the generic type of the List<Comparable<?>> o property is not fixed, I'd get rid of the generic type and rely on the raw type. It costs one @SuppressWarnings("rawtypes"), but it minimizes a lot.

@Override
@SuppressWarnings("rawtypes")
public int compareTo(final Os that) {
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if (that == null)
        return AFTER;
    if (this == that)
        return EQUAL;

    int subresult = ((Integer) this.o.size()).compareTo(that.o.size());

    if (subresult != EQUAL)
        return subresult;

    for (int i = 0; i < this.o.size(); i++) {
        Comparable othis = this.o.get(i);
        Comparable othat = that.o.get(i);
        subresult = othis.compareTo(othat);

        if (subresult != EQUAL)
            return subresult;
    }

    return EQUAL;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文