Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify KeyAndType equals / hashCode #705

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -186,15 +186,15 @@ public <T> Property<T> get(String key, Type type) {
}

private <T> Property<T> getFromSupplier(String key, Type type, Supplier<T> supplier) {
return getFromSupplier(new KeyAndType<T>(key, type), supplier);
return getFromSupplier(new KeyAndType<>(key, type), supplier);
}

@SuppressWarnings("unchecked")
private <T> Property<T> getFromSupplier(KeyAndType<T> keyAndType, Supplier<T> supplier) {
return (Property<T>) properties.computeIfAbsent(keyAndType, (ignore) -> new PropertyImpl<T>(keyAndType, supplier));
return (Property<T>) properties.computeIfAbsent(keyAndType, (ignore) -> new PropertyImpl<>(keyAndType, supplier));
}
private class PropertyImpl<T> implements Property<T> {

private final class PropertyImpl<T> implements Property<T> {
private final KeyAndType<T> keyAndType;
private final Supplier<T> supplier;
private final AtomicStampedReference<T> cache = new AtomicStampedReference<>(null, -1);
Expand Down Expand Up @@ -259,40 +259,45 @@ public synchronized void run() {
}

@Deprecated
@Override
public void addListener(PropertyListener<T> listener) {
Subscription cancel = onChange(new Consumer<T>() {
@Override
public void accept(T t) {
listener.accept(t);
}
});
oldSubscriptions.put(listener, cancel);
oldSubscriptions.put(listener, onChange(listener));
}

/**
* Remove a listener previously registered by calling addListener
* @param listener
*/
@Deprecated
@Override
public void removeListener(PropertyListener<T> listener) {
Optional.ofNullable(oldSubscriptions.remove(listener)).ifPresent(Subscription::unsubscribe);
Subscription subscription = oldSubscriptions.remove(listener);
if (subscription != null) {
subscription.unsubscribe();
}
}

@Override
public Property<T> orElse(T defaultValue) {
return new PropertyImpl<T>(keyAndType, () -> Optional.ofNullable(supplier.get()).orElse(defaultValue));
return new PropertyImpl<>(keyAndType, () -> {
T value = supplier.get();
return value != null ? value : defaultValue;
});
}

@Override
public Property<T> orElseGet(String key) {
if (!keyAndType.hasType()) {
throw new IllegalStateException("Type information lost due to map() operation. All calls to orElse[Get] must be made prior to calling map");
}
KeyAndType<T> keyAndType = this.keyAndType.withKey(key);
Property<T> next = DefaultPropertyFactory.this.get(key, keyAndType.type);
return new PropertyImpl<T>(keyAndType, () -> Optional.ofNullable(supplier.get()).orElseGet(next));
return new PropertyImpl<>(keyAndType, () -> {
T value = supplier.get();
return value != null ? value : next.get();
});
}

@Override
public <S> Property<S> map(Function<T, S> mapper) {
return new PropertyImpl<>(keyAndType.discardType(), () -> {
Expand All @@ -310,7 +315,7 @@ public String toString() {
return "Property [Key=" + getKey() + "; value="+get() + "]";
}
}

private static final class KeyAndType<T> {
private final String key;
private final Type type;
Expand All @@ -321,46 +326,48 @@ public KeyAndType(String key, Type type) {
}

public <S> KeyAndType<S> discardType() {
return new KeyAndType<S>(key, null);
if (type == null) {
@SuppressWarnings("unchecked") // safe since type is null
KeyAndType<S> keyAndType = (KeyAndType<S>) this;
return keyAndType;
}
return new KeyAndType<>(key, null);
}

public KeyAndType<T> withKey(String newKey) {
return new KeyAndType<T>(newKey, type);
return new KeyAndType<>(newKey, type);
}

public boolean hasType() {
return type != null;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = 31 * result + Objects.hashCode(key);
result = 31 * result + Objects.hashCode(type);
return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just return Objects.hash(key, type)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't use it because it's a vararg method, so allocates an array. Maybe it doesn't matter here, or would get inlined by the JIT anyway, I don't know.

}

@Override
public boolean equals(Object obj) {
if (this == obj)
if (this == obj) {
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyAndType other = (KeyAndType) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
}
if (!(obj instanceof KeyAndType)) {
return false;
return true;
}
KeyAndType<?> other = (KeyAndType<?>) obj;
return Objects.equals(key, other.key) && Objects.equals(type, other.type);
}

@Override
public String toString() {
return "KeyAndType{" +
"key='" + key + '\'' +
", type=" + type +
'}';
}
}
}
Loading