Is there a way to define a method in an interface where implementations can be typed to the implementing class? Here's a concrete example of my initial attempt to solve it, but I'll point out where it fails.
public interface ErrorMeasurable<T extends ErrorMeasurable<T>> {
// I want every implementation of this method to accept
// the implementing type as a parameter. I'm willing to accept
// that programmers can maliciously type it to something else
// that meets the type boundary. I'm not willing to accept
// polymorphic usage to be verbose or strange as the below example 开发者_Go百科shows.
boolean approxEquals(T that);
}
// This works...
public class MyDecimal implements ErrorMeasurable<MyDecimal> {
boolean approxEquals(MyDecimal that) { ... }
}
// But polymorphism doesn't work well...
public interface MyNumber<T extends ErrorMeasurable<T>> implements ErrorMeasurable<T> {
boolean approxEquals(T that) { ... }
}
public class MyDecimal2 implements MyNumber<MyDecimal> {
boolean approxEquals(MyDecimal that) { ... }
}
public class UsesNumbers {
// PROBLEM 1: the type parameter is unbound.
MyNumber rawNumber1, rawNumber2;
// PROBLEM 2: This binds the type parameter but it is hard to understand why its typed to itself
MyNumber<MyNumber> boundNumber1, boundNumber2;
void example() {
// PROBLEM 3: There will be a compiler warning about the raw types.
rawNumber1.approxEquals(rawNumber2);
// Works without warnings, see PROBLEM 2 though.
boundNumber1.approxEquals(boundNumber2);
}
}
I don't think what you want can be done with the current level of generics in Java.
You cannot do anything about stopping others using generics as raw types, but that will at least issue a warning. If there aren't warnings, code is guaranteed to be typesafe.
For problem #2, what you probably want is:
public interface MyNumber<T extends MyNumber<T>> implements ErrorMeasurable<T> {
boolean approxEquals(T that) { ... }
}
public class MyDecimal2 implements MyNumber<MyDecimal2> {
boolean approxEquals(MyDecimal2 that) { ... }
}
public class MyDecimal3 extends MyDecimal2 {
boolean approxEquals(MyDecimal2 that) { ... }
}
public class UsesNumbersClass<T extends MyNumber<T>> {
T boundNumber1, boundNumber2;
void example() {
boundNumber1.approxEquals(boundNumber2);
}
}
public class UsesNumbersOnlyMethod {
<T extends MyNumber<T>> void example(T boundNumber1, T boundNumber2) {
boundNumber1.approxEquals(boundNumber2);
}
}
Every class can have only single actual implementation of method boolean approxEquals(T that)
, so you have to decide for each class if you want that method to represent exactly that class (like MyDecimal2
), or you want it to be able to handle broader types - some parent type (like MyDecimal3
). Almost always you would want it to handle exactly that class.
精彩评论