Home > Mobile >  Object-Equals to force same type at runtime
Object-Equals to force same type at runtime

Time:12-10

I am wondering whether it is possible to add something like Objects.equals() that allows runtime checking of types.
Note: I know, that is not what you always want to do, but I think it has its use cases. At least for me.

Example problem: I have some class, let's say, an id of type Integer. And I have an entity Foo with foo.getId() returns a type Integer. For some reasons I check for equality with Objects.equals(someId, foo.getId()).
Now I refactor my entity, foo.getId() will no longer return an Integer but will return Long. Unfortunatelly, there will be no compile time hint at all that Objects.equals(someId, foo.getId()) will never return true. (Yes, stuff like sonarqube helps you a bit).

To solve that, I thought I write something like

private static <T> boolean equals(T object1, T object2) {
    return Objects.equals(object1, object2);
}

which.....just does not work. It still accepts any arbitrary Object. Is there any possible solution in Java for this?

Edit: Please note, this question has nothing to do with equals and hashCode of an object or overriding equals of an object in general, I am looking for a compile-time solution.

CodePudding user response:

I think there is no fast solution short of writing a bunch of equals() methods. This might be feasible if you have only a limited set of types that you want to use:

public class Equals {

    public static boolean equals(Integer a, Integer b) {
        return Objects.equals(a, b);
    }

    public static boolean equals(Long a, Long b) {
        return Objects.equals(a, b);
    }

    public static void main(String[] args) {
        // System.out.println(equals(1, 1L)); doesn't compile
        System.out.println(equals(1, 1));
        System.out.println(equals(1L, 1L));
    }
}

CodePudding user response:

Just as Thomas already mentioned, I cannot think of a fast solution either.

You have several options, some of which you may have already tried:

  • You may use your own equals methods, forcing you to pass two arguments of the same type (either both Integer or both Long). This is the solution Thomas proposed.

  • You may force a certain type by typecasts:

    Objects.equals((Integer) someId, (Integer) foo.getId())
    

    The compiler starts whining if you change the type of either of the arguments.

  • Apply encapsulation. Create a class Id, in which you store the actual value, either as a Long or as an Integer. Then if you return an id anywhere, use the Id class for it. The problem with directly using a Long or Integer is that you expose the internal implementation. And when you change the implementation, you change the interface.

    Whether this option is feasible, depends on your use case.

If you don't want to use a class Id, then I suggest you switch to Long, and hope you don't ever need an id greater than 9,223,372,036,854,775,807.

  • Related