@@ -12,59 +12,67 @@ Let's see how we can accomplish this task in both languages, and what parallels
1212## C++ approach
1313
1414In C++ this is quite straight forward: you just need to specify the * signature * that function has to satisfy
15-
16- template ** \< ** typename R** > ; **
17- ** using **** callable_type **** = ** R ** (* )(** std** ::** string** );**
15+
16+ ``` c++
17+ template <typename R>
18+ using callable_type = R (*)(std::string);
19+ ```
1820
1921where the * callable_type* defines the signature of the ** free function** .
20-
2122At the client side
22-
23- ** void **** func**** (** std** ::** vector** \< ** std** ::** string** > ; ** input** , **** callable_type**** \< **** void**** > ; ** callback** ) {**
24- ** for **** (**** const ** auto** & ; ** in ** :** input** ) {**
25- callback** (** in** );**
23+
24+ ``` c++
25+ void func (std::vector< std::string > input, callable_type<void > callback) {
26+ for (const auto in : input) {
27+ callback (in);
2628 // std::invoke(callback, in);
27- ** } **
28- ** } **
29-
29+ }
30+ }
31+ ```
3032our higher-order function can lift this - visiting all elements in array and applying the same functionality accordingly.
3133This way, our callable type becomes the *customization poin*t (Strategy Design Pattern): any callable that
3234satisfies the signature, can be applied (!)
3335
3436If we want to restrict to the *non-static member function* of a particular UDT, we can redefine it as
35-
36- template ** \< ** typename R** , ** class T** > ; **
37- ** using **** callable_type **** = ** R ** (** T** ::* )(** std** ::** string** );**
38-
39- @note *** std::function*** is universal, polymorphic placeholder for any callable: but this is out of the scope right now.
40-
41- template ** \< ** class T** > ; **
42- ** void **** func**** (** std** ::** vector** \< ** std** ::** string** > ; ** input** , **** callable_type**** \< **** void**** > ; ** callback** , ** T** & ; ** obj** ) {**
43- ** for **** (**** const ** auto** & ; ** in ** :** input** ) {**
44- ** (** obj** .*** callback** )(** in** );**
45- // (ptr-> ; * callback)(in); // in case that we pass the pointer
37+
38+ ```c++
39+ template <ypename R, class T>
40+ using callable_type = R (T::)(std::string);
41+ ```
42+ @note std::function is universal, polymorphic placeholder for any callable: but this is out of the scope right now.
43+
44+ ``` c++
45+ template <class T >
46+ void func (std::vector< std::string > input, callable_type<void > callback, T& obj) {
47+ for (const auto& in : input) {
48+ (obj.* callback)(in);
49+ // (ptr->* callback)(in); // in case that we pass the pointer
4650 // std::invoke(callback, obj, in);
47- ** }**
48- ** }**
51+ }
52+ }
53+ ```
4954
5055Back to signature.
5156It becomes even more obvious using the *std::invoke* utility function, that the instance of the UDT needs to be the
5257very first argument of any non-static member function invocation.
5358Welcome to the world of OO programming.
5459
55- ###
5660### Variadic arguments pack
5761
5862Where C++ prevails is that with C++ we can specify really generic: universal function signature,
5963with arbitrary number of arguments - even of a different type, using **variadic arguments** pack
60-
61- template ** \< ** typename R** , ** typename** ...** Args** > ; **
62- ** using **** universal_callback_type **** = ** R ** (* )(** Args** & ;& ; ...**** );**
64+
65+ ```c++
66+ template <typename R, typename...Args>
67+ using universal_callback_type = R (*)(Args&&...);
68+ ```
6369
6470@note We can also add the * const qualifier* to signature - to make the function's enclosing type T immutable
65-
66- template ** \< ** typename R** , ** typename T** ,** typename** ...** Args** > ; **
67- ** using **** universal_callback_type **** = ** R ** (** T** ::***** )(**** const** Args** & ;& ; ...) **** const**** ;**
71+
72+ ``` c++
73+ template <typename R, typename T, typename ...Args>
74+ using universal_callback_type = R (T::*)(const Args&&...) const ;
75+ ```
6876
6977@note Actually, we can add * volatile* qualifier as well - which means, that the function will be called on the
7078volatile instance of the enclosing class T
@@ -73,17 +81,21 @@ volatile instance of the enclosing class T
7381
7482In C++ - one can also specify explicitly - as part of the function signature, whether
7583the function may throw
76-
77- template ** \< ** typename R** > ; **
78- ** using **** callback_type **** = ** R ** (* )(**** void**** ) **** throw **** (** std** ::** logic_error** );**
84+
85+ ``` c++
86+ template <typename R>
87+ using callback_type = R (*)(void ) throw (std::logic_error);
88+ ```
7989
8090Starting with C++11 - this is considered deprecated.
8191Instead - assuming that every function (except destructor) can implicitly throw, as a hint to compiler
8292there is a new operator *** noexcept *** that indicates whether the function may throw - or not
8393(noexcept == noexcept(true)): it can be conditionally expressed
84-
85- template ** \< ** typename R** ,** typename Arg** > ; **
86- ** using **** callback_type **** = ** R ** (* )(** Arg** ) **** noexcept **** (** std** ::** is_nothrow_copy_constructible_v** \< ** Arg** > ; );**
94+
95+ ``` c++
96+ template <typename R, typename ...Args>
97+ using callback_type = R (*)(Arg&&...) noexcept (std::is_nothrow_copy_constructible_v<Args> &&...);
98+ ```
8799
88100As a consequence - there will be no stack unwinding in order to propagate the exception to the
89101outer functions that presumably catch and handle exception: but rather if the exception is thrown - the program
@@ -96,57 +108,68 @@ So, how we can accomplish the same with Java?
96108And Java is indeed the pure OO language.
97109
98110In Java, we can specify a custom ** callback interface**
99-
111+
112+ ``` java
100113@FunctionalInterface
101- ** interface **** CallbableType**** \< ** R, T** > ; {**
102- R apply** (T obj);**
103- ** }**
114+ interface CallbableType <R, T> {
115+ R apply (T obj );
116+ }
117+ ```
104118
105119This would be equivalent to defining the non-static member function of T, that is ** parameterless** - since the very first argument
106120must be the instance on which the method will be invoked.
107121
108122Then, we define the higher-order function as
109-
110- ** \< ** R, T** > ; ** R ** func**** (** @NonNull ** CallableType**** \< ** R, T** > ; ** callback, T obj** ) {**
123+
124+ ``` java
125+ < R , T > R func(@NonNull CallableType < R , T > callback, T obj) {
111126 // do something
112- ** return ** callback** .** apply** (** obj** );**
113- ** }**
127+ return callback. apply(obj);
128+ }
129+ ```
114130
115131This is similar calling the std::invoke, providing the instance of T, as a first argument
116132
117133We can, on the place where * callback* is expected, provide:
118- > - Reference to the non-static member function of the enclosing class
119- > ** this**** ::\< ** function** > ; **
120- > - Reference to the non-static member function of another class, for which we need to provide argument as well
121- > ** \< ** Class** > ; ::\< ** function** > ; **
122- > - We can provide the ** lambda** object on the fly, that satisfies the signature
123- > ** (** obj** )-> ; {**
124- > // do something
125- > ** return** obj** .\< ** func** > ; ();**
126- > ** }**
127- >
128-
134+ - Reference to the non-static member function of the enclosing class
135+ ``` java
136+ this :: < function>
137+ ```
138+ - Reference to the non-static member function of another class, for which we need to provide argument as well
139+ ``` java
140+ < Class > :: < function>
141+ ```
142+ - We can provide the lambda object on the fly, that satisfies the signature
143+
144+ ``` java
145+ (obj)- > {
146+ // do something
147+ return obj. < func> ();
148+ }
149+ ```
129150Pay attention - we don't explicitly implement the functional interface: we use it as a * placeholder* for providing
130151the already existing callables that satisfy the signature
131-
132- ** private **** \< ** R** > ; ** List** \< ** R** > ; **** transform**** (** @NonNull List** \< ** Person** > ; ** list** , ** @NonNull ** CallableType**** \< ** R** ,** Person** > ; ** callable** ) {**
133- ** return** list** .** stream** ().** map** (**** callable**** ::**** apply**** ).** collect** (** toList** ());**
134- ** }**
135-
136- @Test
137- ** public void ** testTransformPersonToName** () {**
138- List** \< ** Person** > ; ** people ** =** List** .** of** (**** new ** Person** (** "Alice"** , **** 25**** ), **** new ** Person** (** "Bob"** , **** 30**** ));**
139-
140- transform** (** people** , ** Person** ::** getName** ).** forEach** (** System** .** out** ::** println** ); ** // Reference to method
141- transform** (** people** , ** person** -> ; ** person** .** getAge** ()**** ).** forEach** (** System** .** out** ::** println** ); ** // Lambda expression
142- ** }**
152+
153+ ``` java
154+ private < R > List<R > transform(@NonNull List<Person > list, @NonNull CallableType<R , Person > callable) {
155+ return list. stream(). map(callable:: apply). collect(toList());
156+ }
157+
158+ @Test
159+ public void testTransformPersonToName() {
160+ List<Person > people = List . of(new Person (" Alice" , 25 ), new Person (" Bob" , 30 ));
161+
162+ transform(people, Person :: getName). forEach(System . out:: println); // Reference to method
163+ transform(people, person - > person. getAge()). forEach(System . out:: println); // Lambda expression
164+ }
165+ ```
143166
144167@note C++ introduced the * ranges* in C++20. Java has * streams* since Java 8 SDK.
145168
146169As matter of fact - there are already predefined* Functional Interfaces* which are part of the * java.util.function * package,
147170that is introduced with Java 8 SDK.
148171
149- * Function\ < R, T& gt ; * offers the same * apply* () callback as our manually written interface.
172+ Function<R, T> offers the same apply() callback as our manually written interface.
150173
151174Actually, it's callbacks interface - since it provides the signature for three additional callbacks.
152175It's even composable, since you can instantiate it with the callable that will be applied first, and
@@ -158,54 +181,59 @@ the functional programming style in Java.
158181### Exception in signature
159182
160183Interesting enough, the Exception Type can be also part of the function signature in Java
161- **
162- @FunctionalInterface
163- ** interface **** CallableType**** \< ** R** ,** T** > ; {**
164- R apply** (** T obj** ) **** throws ** RemoteException** ;**
165- ** }**
166-
184+
185+ ``` java
186+ @FunctionalInterface
187+ interface CallableType <R, T> {
188+ R apply (T obj ) throws RemoteException ;
189+ }
190+ ```
167191or to be generic as possible
168-
169- @FunctionalInterface
170- ** interface **** CallableType**** \< ** R** ,** T** ,** E ** extends** Exception** > ; {**
171- R apply** (** @NonNull T obj** ) **** throws** E** ;**
172- ** }**
173-
192+
193+ ``` java
194+ @FunctionalInterface
195+ interface CallableType <R, T, E extends Exception > {
196+ R apply (@NonNull T obj ) throws E ;
197+ }
198+ ```
174199Our higher-order function may preserve the exception indication in its own signature
175-
176- ** \< ** T** ,** R** ,** E ** extends** Exception** > ; ** R ** invoke**** (**** CallableType**** \< ** T** ,** R** ,** E** > ; ** f** ,** T arg** ) **** throws** E ** {**
177- // do something
178- ** return** f** .** apply** (** arg** );**
179- ** }**
180-
200+
201+ ``` java
202+ < T , R , E extends Exception > R invoke(CallableType<T , R , E > f, T arg) throws E {
203+ // do something
204+ return f. apply(arg);
205+ }
206+ ```
181207or it can handle it internally.
182208
183209In case that due to * interoperability* - the API expects the Function Interface which is not throwable, and we use the one which may throw, we can write the safe wrapper to bridge it
184-
185- ** static **** \< ** T** ,** R** > ; **** Function**** \< ** T** ,** R** > ; **** safeWrap**** (**** ThrowingFunction**** \< ** T** ,** R** ,** ?** > ; ** f** ) {**
186- ** return** t ** -> ; {**
187- ** try **** {**
188- ** return** f** .** apply** (** t** );**
189- ** } **** catch **** (** RuntimeException e** ) {**
190- ** throw** e** ; ** // Re-throw unchecked exceptions
191- ** } **** catch **** (** Exception e** ) {**
192- ** throw new ** IllegalStateException** (** "Unexpected checked exception"** ,** e** );**
193- ** }**
194- ** };**
195- ** }**
196-
210+
211+ ``` java
212+ static < T , R > Function<T , R > safeWrapper(ThrowingFunction<T , R , ?> f) {
213+ return t - > {
214+ try {
215+ return f. apply(t);
216+ } catch (RuntimeException e) {
217+ throw e; // Re-throw unchecked exceptions
218+ } catch (Exception e) {
219+ throw new IllegalStateException (" Unexpected checked exception" , e);
220+ }
221+ };
222+ }
223+ ```
197224
198225### Variadic arguments pack
199226
200227Unlike C++ - Java doesn't support variadic arguments pack, at least not for expressing the arbitrary number of
201228heterogenous types.
202229It supports only variadic arguments list of the same type
203-
230+
231+ ``` java
204232@FunctionalInterface
205- ** interface **** CallableType**** \< ** R ** , ** T ** , ** E ** extends** Exception** & gt ; { **
206- R apply** ( ** @NonNull T** ...** objs** ) **** throws** E ** ; **
207- ** } **
208-
233+ interface CallableType <R, T, E extends Exception > {
234+ R apply ( @NonNull T ...objs ) throws E ;
235+ }
236+ ```
209237
210238## Conclusion
211239
@@ -214,8 +242,8 @@ programming was not originally part of the language core - it's added afterwards
214242* type erasur* e (stripping all type information - and treating it as Object inside the function template).
215243
216244More on ** type erasure** , and different interpretations - in different languages:
217- > - [ https://github.com/damirlj/modern_cpp_tutorials?tab=readme-ov-file#tut7 ] ( https://github.com/damirlj/modern_cpp_tutorials?tab=readme-ov-file#tut7 )
218- >
245+ [ Type Erasure ] ( https://github.com/damirlj/modern_cpp_tutorials?tab=readme-ov-file#tut7 )
246+
219247
220248The only where Java is inferior in fulfilling this particular task - is the fact that heterogenous variadic argument pack is not
221249supported (nor the fold expressions, type-traits, auto type deduction and all other mechanics of template metaprogramming).
0 commit comments