Skip to content

Commit 25e83af

Browse files
author
Julien Letrouit
committed
Finished AboutInterfaces in english
1 parent a5f1318 commit 25e83af

12 files changed

Lines changed: 624 additions & 20 deletions

File tree

src/main/java/bonuses/english/AboutInterfaces.java

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package bonuses.english;
22

33
import java.util.List;
4+
import java.util.function.IntPredicate;
45

6+
import bonuses.teachingmaterial.Combining;
57
import engine.Locale;
68
import engine.Sensei;
79
import sensei.AboutInterfacesKoans;
@@ -54,7 +56,7 @@ public class AboutInterfaces {
5456
* // Since Sword implements Weapon, it must implement the hit method.
5557
* @Override // This special annotation means the method is defined elsewhere (our interface in this case)
5658
* public void hit(Monster monster) {
57-
* // some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
59+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
5860
* }
5961
*
6062
* }
@@ -91,6 +93,173 @@ public class AboutInterfaces {
9193
*
9294
*/
9395

96+
97+
/**
98+
* # Anonymous interface implementation
99+
*
100+
* Write a method 'getAnonymousCombining' which returns an anonymous implementation of 'bonuses.teachingmaterial.Combining'.
101+
* The implementation of the combine() method should return the second number subtracted from the first.
102+
*
103+
* --------- TIPS --------------
104+
*
105+
* Sometimes, it is tedious to create a class file to implement an interface just for one occasion.
106+
* In these situation, you can implement the interface in an anonymous class. It is anonymous, because it does not have a name.
107+
* That class is instantiated immediately where it is created. For example:
108+
*
109+
* public Weapon toss() {
110+
* return new Weapon() {
111+
* @Override
112+
* public void hit(Monster monster) {
113+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
114+
* }
115+
* }
116+
* }
117+
*
118+
* When looking at this code, you could be tempted to believe there is a constructor for the interface Weapon, but there is not.
119+
* We are really creating a class, for which there exists a single object.
120+
* The constructor with empty parameters is the one of this nameless class.
121+
* We can now get and use the tossed weapon this way:
122+
*
123+
* Weapon tossedWeapon = toss();
124+
* tossedWeapon.hit(zombie);
125+
*
126+
* -------------------------------
127+
*
128+
* Expected result:
129+
*
130+
* getAnonymousCombining().combine(3, 4) should return -1
131+
*
132+
*/
133+
134+
135+
/**
136+
* # Lambda methods
137+
*
138+
* Write a method 'getLambdaCombining' which returns an lambda method implementing 'bonuses.teachingmaterial.Combining'.
139+
* The implementation of the combine() method should return the first number subtracted from the second.
140+
*
141+
* --------- TIPS --------------
142+
*
143+
* When an interface have only one method, there is an even shorter form to implement it. You can use what is called a 'lambda method'.
144+
* A 'lambda method' is a stripped down version of a method. Since our example interface 'Weapon' has only a single method 'hit()', we can use this shortcut:
145+
*
146+
* For example:
147+
*
148+
* public Weapon toss() {
149+
* return (monster) -> {
150+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc..
151+
* };
152+
* }
153+
*
154+
* We can now get and use the tossed weapon this way:
155+
*
156+
* Weapon tossedWeapon = toss();
157+
* tossedWeapon.hit(zombie);
158+
*
159+
* The general syntax for lambda method returning void, or having a body with multiple lines:
160+
*
161+
* ([param1Name], [param2Name], ...) -> {
162+
* // Lambda method body here
163+
* }
164+
*
165+
* If your lambda is having a single expression, you can even skip the parentheses and the 'return':
166+
*
167+
* ([param1Name], [param2Name], ...) -> // expression here
168+
*
169+
* Here are some example of methods and their lambda equivalent (assuming the interface has only one method in it):
170+
*
171+
* This interface implementation:
172+
*
173+
* public void sayHello() {
174+
* System.out.println("hello");
175+
* }
176+
*
177+
* Can be replaced by this lambda:
178+
*
179+
* () -> {
180+
* System.out.println("hello");
181+
* }
182+
*
183+
* This interface implementation:
184+
*
185+
* public int square(int x) {
186+
* return x * x;
187+
* }
188+
*
189+
* Can be replaced by this lambda:
190+
*
191+
* (x) -> x * x
192+
*
193+
* This interface implementation:
194+
*
195+
* public int min(int x, int y) {
196+
* if (x < y) {
197+
* return x;
198+
* }
199+
* return y;
200+
* }
201+
*
202+
* Can be replaced by this lambda:
203+
*
204+
* (x, y) -> {
205+
* if (x < y) {
206+
* return x;
207+
* }
208+
* return y;
209+
* }
210+
*
211+
* -------------------------------
212+
*
213+
* Expected result:
214+
*
215+
* getLambdaCombining().combine(3, 4) should return 1
216+
*
217+
*/
218+
219+
220+
/**
221+
* # Common lambda interfaces
222+
*
223+
* Write a method 'getIsEven' which returns a lambda method testing if an integer is even.
224+
*
225+
* --------- TIPS --------------
226+
*
227+
* Since lambda methods are so useful, a lot of interfaces already exist in the Java standard library, and we don't have to create them ourselves.
228+
*
229+
* For example:
230+
*
231+
* For a lambda taking no parameter, and returning nothing, {@link java.lang.Runnable}:
232+
*
233+
* Runnable sayHello = () -> { System.out.println("Hello"); };
234+
*
235+
* For a lambda taking a int parameter, and returning nothing, {@link java.util.function.IntConsumer}:
236+
*
237+
* IntConsumer displayInt = (anInt) -> { System.out.println(anInt); };
238+
*
239+
* The same exist for other type. For example {@link java.util.function.DoubleConsumer}:
240+
*
241+
* DoubleConsumer displayDouble = (aDouble) -> { System.out.println(aDouble); };
242+
*
243+
* The reverse functions, taking nothing as a parameter, but returning something exist as well: {@link java.util.function.IntSupplier}, {@link java.util.function.DoubleSupplier}. etc...
244+
*
245+
* DoubleSupplier giveMePiPleeeaaase = () -> 3.14159;
246+
*
247+
* There is also a lot of case where you would need to test a number somehow. This is where interfaces like {@link java.util.function.IntPredicate} shine:
248+
*
249+
* IntPredicate isPositive = (number) -> number >= 0;
250+
*
251+
* For the exercise, you can use the modulo operator, %, which computes the remainder of an integer division:
252+
*
253+
* int remainder = 17 % 5; // remainder equals 2
254+
*
255+
* -------------------------------
256+
*
257+
* Expected result:
258+
*
259+
* getIsEven().test(4) should return true
260+
*
261+
*/
262+
94263

95264
public static void main(String[] args) {
96265
new Sensei(Locale.en, List.of(AboutInterfacesKoans.koans)).offerKoans();

src/main/java/bonuses/teachingmaterial/Combining.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ public interface Combining {
77
/**
88
* somehow combines 2 integers a and b, and returns the result of this combination.
99
*/
10-
int combine(int a, int b);
10+
public int combine(int a, int b);
1111
}

src/main/java/engine/Assertions.java

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,58 @@ private static ResultAssertion assertReturnValueWithRandomEquals(final Function<
273273
return true;
274274
};
275275
}
276+
277+
public static ResultAssertion assertReturnValueIsAnonymousObject() {
278+
return (p, res) -> {
279+
if (res.executionResult == null) {
280+
p.println(format(EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED_NULL, Formats.Red, code(res.resultExpressionSourceCode)));
281+
return false;
282+
} else if (res.executionResult.getClass().getSimpleName().contains("$$Lambda$")) { // Kind of hacky, but only way as far as I know
283+
p.println(format(EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED_LAMBDA, Formats.Red, code(res.resultExpressionSourceCode)));
284+
return false;
285+
} else if (!res.executionResult.getClass().isAnonymousClass()) {
286+
p.println(format(EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED, Formats.Red, code(res.resultExpressionSourceCode), code(res.executionResult.getClass().getName())));
287+
return false;
288+
}
289+
290+
p.println(format(OK_RETURNED_OBJECT_IS_ANONYMOUS, Formats.Green, code(res.resultExpressionSourceCode)));
291+
return true;
292+
};
293+
}
294+
295+
public static ResultAssertion assertReturnValueIsLambda() {
296+
return (p, res) -> {
297+
if (res.executionResult == null) {
298+
p.println(format(EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED_NULL, Formats.Red, code(res.resultExpressionSourceCode)));
299+
return false;
300+
} else if (res.executionResult.getClass().isAnonymousClass()) {
301+
p.println(format(EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED_ANONYMOUS, Formats.Red, code(res.resultExpressionSourceCode)));
302+
return false;
303+
} else if (!res.executionResult.getClass().getSimpleName().contains("$$Lambda$")) { // Kind of hacky, but only way as far as I know
304+
p.println(format(EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED, Formats.Red, code(res.resultExpressionSourceCode), code(res.executionResult.getClass().getName())));
305+
return false;
306+
}
307+
308+
p.println(format(OK_RETURNED_OBJECT_IS_LAMBDA, Formats.Green, code(res.resultExpressionSourceCode)));
309+
return true;
310+
};
311+
}
312+
313+
public static ResultAssertion assertReturnValueImplements(Class<?> valueInterface) {
314+
return (p, res) -> {
315+
final var interfaceName = code(valueInterface.getName());
316+
if (res.executionResult == null) {
317+
p.println(format(EXPECTED_TO_RETURN_IMPLEMENTING_BUT_RETURNED_NULL, Formats.Red, code(res.resultExpressionSourceCode), interfaceName));
318+
return false;
319+
} else if (!valueInterface.isInstance(res.executionResult)) {
320+
p.println(format(EXPECTED_TO_RETURN_IMPLEMENTING_BUT_NOT, Formats.Red, code(res.resultExpressionSourceCode), interfaceName, code(res.executionResult.getClass().getName())));
321+
return false;
322+
}
323+
324+
p.println(format(OK_RETURNED_OBJECT_IMPLEMENTS, Formats.Green, code(res.resultExpressionSourceCode), interfaceName));
325+
return true;
326+
};
327+
}
276328

277329
public static BeforeTestAssertion assertConstructorIsInvokable(final String className, final Type... constructorParamTypes) {
278330
return (p, locale, _koan) -> {
@@ -325,7 +377,7 @@ public static BeforeTestAssertion assertConstructorIsInvokable(final String clas
325377
return true;
326378
};
327379
}
328-
380+
329381
/**
330382
* This asserts that the method in the current Koan class is invokable with the given param types.
331383
*
@@ -443,9 +495,7 @@ public static BeforeTestAssertion assertImplementsInterface(final String classNa
443495
return (p, locale, koan) -> {
444496
final var type = new Type(className);
445497
final var clasz = type.unsafeResolve();
446-
final var success = Arrays
447-
.stream(clasz.getInterfaces())
448-
.anyMatch(implemented -> implemented == interfaceClass);
498+
final var success = interfaceClass.isAssignableFrom(clasz);
449499

450500
if (!success) {
451501
p.println(format(EXPECTED_CLASS_TO_IMPLEMENT, Formats.Red, code(className), code(interfaceClass.getName())));

src/main/java/engine/Sensei.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ private boolean offerTest(KoanTest test, int successfulCount) {
104104
// Can't detect infinite loop when the user has to enter data through the console, because we don't know how much time to wait for.
105105
thread.join();
106106
} else {
107-
thread.join(TIMEOUT_INFINITE_LOOPS_MS);
107+
thread.join();
108+
//thread.join(TIMEOUT_INFINITE_LOOPS_MS);
108109
}
109110
}
110111
catch(InterruptedException ie) {

src/main/java/engine/Texts.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,30 @@ public class Texts {
153153
public static Localizable<String> EXPECTED_TO_RETURN_BUT_RETURNED_OTHER_TYPE =
154154
local("Expected %s to return a %s but returned a %s instead!")
155155
.fr("Attendu à ce que %s retourne un %s mais a retourné un %s à la place!");
156+
public static Localizable<String> EXPECTED_TO_RETURN_IMPLEMENTING_BUT_RETURNED_NULL =
157+
local("Expected %s to return a %s but returned null instead!")
158+
.fr("Attendu à ce que %s retourne un %s mais a retourné null à la place!");
159+
public static Localizable<String> EXPECTED_TO_RETURN_IMPLEMENTING_BUT_NOT =
160+
local("Expected %s to return an object implementing %s but %s is not!")
161+
.fr("Attendu à ce que %s retourne un object implémentant %s mais %s ne l'implémente pas!");
162+
public static Localizable<String> EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED_NULL =
163+
local("Expected %s to return an anonymous implementation but returned null instead!")
164+
.fr("Attendu à ce que %s retourne une implémentation anonyme mais a retourné null à la place!");
165+
public static Localizable<String> EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED =
166+
local("Expected %s to return an anonymous implementation but returned a %s instead!")
167+
.fr("Attendu à ce que %s retourne une implémentation anonyme mais a retourné un %s à la place!");
168+
public static Localizable<String> EXPECTED_TO_RETURN_ANONYMOUS_BUT_RETURNED_LAMBDA =
169+
local("Expected %s to return an anonymous implementation but returned a lambda method instead!")
170+
.fr("Attendu à ce que %s retourne une implémentation anonyme mais a retourné une méthode lambda à la place!");
171+
public static Localizable<String> EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED_NULL =
172+
local("Expected %s to return a lambda method but returned null instead!")
173+
.fr("Attendu à ce que %s retourne une méthode lambda mais a retourné null à la place!");
174+
public static Localizable<String> EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED =
175+
local("Expected %s to return a lambda method but returned a %s instead!")
176+
.fr("Attendu à ce que %s retourne une méthode lambda mais a retourné un %s à la place!");
177+
public static Localizable<String> EXPECTED_TO_RETURN_LAMBDA_BUT_RETURNED_ANONYMOUS =
178+
local("Expected %s to return a lambda method but returned an anonymous implementation instead!")
179+
.fr("Attendu à ce que %s retourne une méthode lambda mais a retourné une implémentation anonyme à la place!");
156180
public static Localizable<String> EXPECTED_VARIABLE_TO_BE_BUT_WAS_OTHER_TYPE =
157181
local("Expected %s to result in %s being a %s but was a %s instead!")
158182
.fr("Attendu à ce que %s résulte en %s étant un %s mais était un %s à la place!");
@@ -168,6 +192,15 @@ public class Texts {
168192
public static Localizable<String> OK_RETURNED =
169193
local("Ok: %s returned %s.")
170194
.fr("Ok: %s a retourné %s.");
195+
public static Localizable<String> OK_RETURNED_OBJECT_IMPLEMENTS =
196+
local("Ok: return of %s implements %s.")
197+
.fr("Ok: le retour de %s implémente %s.");
198+
public static Localizable<String> OK_RETURNED_OBJECT_IS_ANONYMOUS =
199+
local("Ok: return of %s is an anonymous implementation.")
200+
.fr("Ok: le retour de %s est une implémentation anonyme.");
201+
public static Localizable<String> OK_RETURNED_OBJECT_IS_LAMBDA =
202+
local("Ok: return of %s is a lambda method.")
203+
.fr("Ok: le retour de %s est une méthode lambda.");
171204
public static Localizable<String> EXPECTED_TO_RETURN_INT_BUT_RETURNED_OTHER_TYPE =
172205
local("Expected %s to return an integer but returned a '%s' instead!")
173206
.fr("Attendu à ce que %s retourne un entier mais a retourné un '%s' à la place!");

0 commit comments

Comments
 (0)