Question: While assisting a student with a university project, we worked on a Java exercise provided by the university which defined a class for an address with the fields:

```
number
street
city
zipcode

```

And it specified that the equals logic should return true if the number and zip code match.

I was once taught that the equals method should only be doing an exact comparison between the objects (after checking the pointer), which makes some sense to me, but contradicts with the task they were given.

I can see why you would want to override the logic so that you can use things like `list.contains()` with your partial matching but I'm wondering if this is considered kosher, and if not why not?

Answer: 
An equality operator will claim that two objects are equal if and only if they should be considered equal, due to whatever considerations that you find useful. 

I’ll repeat that: Due to whatever considerations that you find useful. 

The software developer is in the driver’s seat here. Apart from being consistent with obvious requirements (a=a, a=b implies b+a, a=b and b=c implies a=c) and consistency with the hash function) the equality operator can be whatever you like.
As others have mentioned, the exact semantics of object equality are a part of the business domain's definition. In this case, I don't think it's reasonable to have a "general" object like `Address` (containing `number`, `street`, `city`, `zipcode`) to having a very narrow definition of equality (which, as others have mentioned, works in Brazil but not in the US, for example).

Instead, I would have `Address` have value-like semantics for equality (defined by equality of all members). I would then either:

1. Create a `StreeNumberAndZip` class (`# TODO: bad name`), which contains only a `street`, and a `zipCode`, and defines `equals` over those. Whenever you want to compare two `Address` objects in that particular way, you can do `addressA.streetNumberAndZip().equals(addressB.streetNumberAndZip())`, or...
2. Create a `AddressUtils` class with a `bool equalStreeNumberAndZipCode(Address a, Address b)` method, which defines the narrow equality there.

In both cases, you still have access to use `addressA.equals(addressB)` for full equality checking.

For `n` fields of an object, there are `2^n` different definitions of equality (each field can be included or excluded from the check). If you find yourself needing to check equality in many different ways, it might also be useful to have something like an `enum AddressComponent`. You could then have a `bool addressComponentsAreEqual(EnumSet<AddressComponent> equatedComponents, Address a, Address b)`, so you can call something like 

```
bool addressAreKindOfEqual = AddressUtils.addressComponentsAreEqual(
    new EnumSet.of(
        AddressComponent.streetNumber, 
        AddressComponent.zipCode,
    ),
    addressA, addressB
);

```

This is obviously a lot more typing, but it can save you from having an exponential explosion of equality-checking methods.