5 ms·
In Java, interface Shape<T> { interface _Circle<T> { T apply(int x, int y, int r); } interface _Rectangle<T> { T apply(int x, int y,
by _old_dude_ 6y ago
In Java,
interface Shape<T> {
interface _Circle<T> {
T apply(int x, int y, int r);
}
interface _Rectangle<T> {
T apply(int x, int y, int w, int h);
}
T apply(_Circle<T> circle, _Rectangle<T> rectangle);
static <T> Shape<T> Circle(int x, int y, int r) {
return (circle, rectangle) -> circle.apply(x, y, r);
}
static <T> Shape<T> Rectangle(int x, int y, int w, int h) {
return (circle, rectangle) -> rectangle.apply(x, y, w, h);
}
static double area(Shape<?> shape) {
var s = (Shape<Double>) shape;
return s.apply(
(x, y, r) -> Math.PI * r * r,
(x, y, w, h) -> 1. * w * h
);
}
static void main(String[] args) {
var exampleCircle = Circle(2, 1, 4);
var exampleRectangle = Rectangle(1, 3, 10, 7);
System.out.println(area(exampleCircle));
System.out.println(area(exampleRectangle));
}
}
BTW, you can use the same encoding when you write a Promise in JS.
- Twisol 6y agoAs with the TypeScript examples, we can make the top-level apply method generic instead of the whole class to avoid some casts. In particular, notice that the <T> inferred from calling Circle() and Rectangle() is the unmeaningful <Object>. The downside is that Java doesn't let you implement generic methods with lambdas, which is silly, so you have to use an anonymous subclass. But at least the call-side uses stay the same. (And then, combining the `_Circle` and `_Rectangle` interfaces gets you to a more canonical visitor pattern, at the expense of lambdas at the call-site.) public interface Shape { interface _Circle<T> { T apply(int x, int y, int r); } interface _Rectangle<T> { T apply(int x, int y, int w, int h); } <T> T apply(_Circle<T> circle, _Rectangle<T> rectangle); static Shape Circle(int x, int y, int r) { return new Shape() { public <T> T apply(_Circle<T> circle, _Rectangle<T> rectangle) { return circle.apply(x, y, r); } }; } static Shape Rectangle(int x, int y, int w, int h) { return new Shape() { public <T> T apply(_Circle<T> circle, _Rectangle<T> rectangle) { return rectangle.apply(x, y, w, h); } }; } static double area(Shape shape) { return shape.apply( (x, y, r) -> Math.PI * r * r, (x, y, w, h) -> 1. * w * h ); } static void main(String[] args) { var exampleCircle = Circle(2, 1, 4); var exampleRectangle = Rectangle(1, 3, 10, 7); System.out.println(area(exampleCircle)); System.out.println(area(exampleRectangle)); } }
- _old_dude_ 6y ago> The downside is that Java doesn't let you implement generic methods with lambdas ... yes, that exactly why i've chosen to make the type parametric and not the method apply, but you're right that the classical visitor pattern uses a polymorphic method (accept) not a polymorphic class.
- Twisol 6y agoSure. Then, if you're set on using lambdas, I'd recommend this, to at least take away the ability to choose <T> from the caller. static Shape<?> Circle(int x, int y, int r) { return (circle, rectangle) -> circle.apply(x, y, r); } static Shape<?> Rectangle(int x, int y, int w, int h) { return (circle, rectangle) -> rectangle.apply(x, y, w, h); }
- dfgdghdf 6y agoThis might get messy once you need hashCode and equals
- _old_dude_ 6y agoYou can keep the Circle and Rectangle as class (record here) and only see them as Shape when you want to do the double dispatch of the visitor. interface Shape { record Circle(int x, int w, int r) {} record Rectangle(int x, int y, int w, int h) {} <T> T apply(Function<Circle, T> _circle, Function<Rectangle, T> _rectangle); static <T> Shape shape(Circle circle) { return new Shape() { @Override public <T> T apply(Function<Circle, T> _circle, Function<Rectangle, T> _rectangle) { return _circle.apply(circle); } }; } static <T> Shape shape(Rectangle rectangle) { return new Shape() { @Override public <T> T apply(Function<Circle, T> _circle, Function<Rectangle, T> _rectangle) { return _rectangle.apply(rectangle); } }; } static double area(Shape shape) { return shape.apply( circle -> Math.PI * circle.r * circle.r, rectangle -> 1. * rectangle.w * rectangle.h ); } static void main(String[] args) { var aCircle = new Circle(2, 1, 4); var aRectangle = new Rectangle(1, 3, 10, 7); System.out.println(area(shape(aCircle))); System.out.println(area(shape(aRectangle))); } }