Talk:Curiously recurring template pattern
![]() | Computer science Start‑class Low‑importance | ||||||||||||||||
|
![]() | C/C++ Unassessed Low‑importance | |||||||||
|
There's a Java version, too
Since Java 1.5 introduced its own implementation of generics, I've noticed a "curiously recurring generics pattern" in Java, but generally geared toward a somewhat different purpose:
public interface Base<D extends Base<D>> { public D someOperation (); public D somethingElse (D argument); public void doSomethingWith (D whatever); }
It's even more common with abstract classes that provide a skeleton implementation, e.g.
public abstract class Vector<D extends Vector<D>> { public abstract int getDimensions(); private void checkDimensions (D other) { if (getDimensions() != other.getDimensions()) throw new Exception(); } public D add (D other) { checkDimensions(other); return addImpl (other); } public D subtract (D other) { checkDimensions(other); return addImpl(other.negated()); } public abstract D addImpl (D other); public abstract D negated (); }
where subclasses just override addImpl (and don't have to do dimension-mismatch detection) and implement negation. The use of the derived type as a parameter for the base type allows several Vector classes to share this code (don't repeat yourself is observed for things they can all do in a common way) while still accepting and returning themselves instead of some abstract type. Prior to Java 5, the pattern couldn't be used and all Vector subclasses would have to return a generic Vector and cope with a generic Vector parameter. So a Vector that was actually a polynomial function, FFT object, or whatever and another that was a plain old coordinate vector could be added with only a run-time ClassCastException to indicate there was a problem, and callers had to cast the return values. Java 5's covariant return types address the latter problem and its generics, coupled with the curiously recurring generics pattern, address the former problem.
(Polynomials, FFTs, and coordinate vectors can, mind you, be abstracted as having an array of components, perhaps in a java.util.Vector, of some length, added componentwise, but the example base class above is superior if you want addable, negatable things that don't have components -- either they don't decompose easily or naturally or they're actually the scalars used to build coordinate vectors -- or sparse representations etc.; FFTs *must* have a sparse representation of some sort, since you can't store the infinitely many coordinates needed for every sub and superharmonic of the fundamental.) —The preceding unsigned comment was added by 74.104.131.76 (talk) 02:21, 2 January 2007 (UTC).
That's a completely different thing that just looks a bit like the CRTP. There is no concept of template instantiation in Java which is what the CRTP hinges on. In the Java example you have a class Vector<D extends Vector<D>> with generic type parameter bounds that refer to the type itself, and all that does is affect which types are correct for the generic methods at compile time, whereas in C++ your class is a template instantiation where the template's type parameter refers to the derived type, meaning you generate a class which refers to its own type despite the code being defined in a template. The benefits of CRTP are not relevant to Java (e.g. there is no virtual/non-virtual distinction and all classes are reference types so there is no use for static polymorphism, and things like polymorphic copying can be done through the reflection API) 89.202.251.196 (talk) 17:16, 3 February 2017 (UTC)