Skip to content

Commit f7a5dc7

Browse files
author
Julien Letrouit
committed
Fully unit tested Expression
1 parent 25e83af commit f7a5dc7

26 files changed

Lines changed: 829 additions & 142 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ We expressed above a concern for saving student's attention / motivation / time.
6464
### Design goals
6565

6666
- Strive to work on a bare WPILib installation: on VSCode with no need for a plugin.
67-
- Simple start: no dependency other than the Java standard library, so as to avoid a build step with a dependency management tool.
67+
- Simple start: no dependency other than the Java standard library, so as to avoid a build step with a dependency management tool. This has consequences: the project has to includes a mini test framework for example.
6868
- Java 17, because as of 2024, this is the version used by default in WPILib's VSCode.
6969

7070
### Compromises and limitations

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ public class AboutInterfaces {
3535
*
3636
* It doesn't matter whether you get a baseball bat or a hammer - as long as it implements your three conditions, you're good.
3737
*
38-
* In Java, you sometimes need an object that implements "conditions": one or more specific methods. But you don't care about its class. The interface is what will describe those methods.
39-
* We can see the interface as a kind of contract that an object would respect.
38+
* In Java, you sometimes need an object of a class you do not care, but which implements "conditions": one or more specific methods.
39+
* We can see the interface as a kind of contract that an object would respect, by the object having implemented the methods listed in the contract.
4040
*
41-
* For example, in a game where the situation above would happen, you could create the following interface in a game:
41+
* For example, in a game where the situation above would happen, you could create the following interface:
4242
*
4343
* public interface Weapon {
4444
* void hit(Monster monster);
@@ -54,22 +54,21 @@ public class AboutInterfaces {
5454
* public class Sword implements Weapon {
5555
*
5656
* // Since Sword implements Weapon, it must implement the hit method.
57-
* @Override // This special annotation means the method is defined elsewhere (our interface in this case)
57+
* @Override // This strange annotation means the method is defined elsewhere (in our interface)
5858
* public void hit(Monster monster) {
5959
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
6060
* }
6161
*
6262
* }
6363
*
64-
*
6564
* Now, the code in hit() maybe complicated but it does not matter: it follows the contract of the interface, and you can call it.
6665
* For example, you could create an object allowing you to hit a zombie with the following code:
6766
*
6867
* Monster zombie = ...; // Code getting the object for the zombie in the middle of the room
6968
* Weapon tossedWeapon = new Sword(); // or new Hammer() or new Axe() or new WhateverImplementsWeapon()
7069
* tossedWeapon.hit(zombie); // use the Weapon interface
7170
*
72-
* Notice the type of the variable 'tossedWeapon': it is a Weapon, not a Sword. Interfaces, like classes, are types you can use for variables, fields, and parameters.
71+
* Notice the type of the variable 'tossedWeapon': it is a Weapon, not a Sword. Interfaces, like classes, are types you can use for declaring variables, fields, and parameters.
7372
* Because Sword implements Weapon, Java considers that a Sword object _is_ a Weapon. Having variables and parameters using the interface type allows you to work with any object implementing that interface.
7473
*
7574
* Take a look at the bonuses.teachingmaterial.Combining interface. It defines a method which can be implemented in various ways.
@@ -93,7 +92,7 @@ public class AboutInterfaces {
9392
*
9493
*/
9594

96-
95+
9796
/**
9897
* # Anonymous interface implementation
9998
*

src/main/java/bonuses/french/AboutInterfaces.java

Lines changed: 219 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,71 +8,252 @@
88

99
public class AboutInterfaces {
1010
/**
11-
* # For loops
11+
* # First interface implementations
1212
*
13-
* Write a method named 'displayNumbers' with an integer parameter 'n', which displays numbers between 1 and n.
14-
* Use a 'for' loop instead of a 'while' loop. Use the shortest form of incrementation / decrementation.
13+
* Write a class 'numbers.AddNumbers' which implements interface 'bonuses.teachingmaterial.Combining'.
14+
* The implementation of the combine() method should return the 2 numbers added together.
15+
* Write a class 'numbers.MultiplyNumbers' which also implements interface 'bonuses.teachingmaterial.Combining'.
16+
* This implementation of the combine() method should return the 2 numbers multiplied together.
1517
*
1618
* --------- TIPS --------------
1719
*
18-
* To do things multiple times in Java, you already know the 'while' loop.
19-
* Most of the time, a 'while' loop has very similar code structure:
20+
* Consider the following situation:
2021
*
21-
* [Initialize a counter];
22-
* while ([Condition on the counter variable]) {
23-
* // Do stuff repetedly
24-
* [Modify the counter at the end of the loop];
22+
* You are in the middle of a large, empty room, when a zombie suddenly attacks you.
23+
* You have no weapon.
24+
* Luckily, a fellow living human is standing in the doorway of the room.
25+
* "Quick!" you shout at him. "Throw me something I can hit the zombie with!"
26+
*
27+
* Now consider:
28+
* You didn't specify (nor do you care) exactly what your friend will choose to toss; ...But it doesn't matter, as long as:
29+
*
30+
* 1) It's something that can be tossed (He can't toss you the sofa)
31+
* 2) It's something that you can grab hold of (Not a wet soap)
32+
* 3) It's something you can use to bash the zombie's brains out (That rules out pillows and such)
33+
*
34+
* It doesn't matter whether you get a baseball bat or a hammer - as long as it implements your three conditions, you're good.
35+
*
36+
* In Java, you sometimes need an object of a class you do not care, but which implements "conditions": one or more specific methods.
37+
* We can see the interface as a kind of contract that an object would respect, by the object having implemented the methods listed in the contract.
38+
*
39+
* For example, in a game where the situation above would happen, you could create the following interface:
40+
*
41+
* public interface Weapon {
42+
* void hit(Monster monster);
2543
* }
2644
*
27-
* Ex:
45+
* Now, whether your weapon is a sword inflicting 10 damage points, or a knife inflicting only 4 damage points to the monster, the player will be able to hit a monster with it.
46+
*
47+
* Respecting the contract of a Java interface is called 'implementing the interface'. A class can implement an interface this way:
48+
*
49+
* you declare that this class will implement the Weapon interface
50+
* vvvvvvvvvvvvvvvvv
51+
*
52+
* public class Sword implements Weapon {
53+
*
54+
* // Since Sword implements Weapon, it must implement the hit method.
55+
* @Override // This strange annotation means the method is defined elsewhere (in our interface)
56+
* public void hit(Monster monster) {
57+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
58+
* }
2859
*
29-
* int times = 3; // Initialize a counter
30-
* while (times > 0) { // Condition on the counter variable
31-
* System.out.println("Still executing"); // Do stuff repetedly
32-
* times = times -1; // Modify the counter at the end of the loop
3360
* }
3461
*
35-
* Since this pattern is used again and again, there is a shortcut in Java to make it terser. It's called a 'for' loop, where all 3 parts seen above are grouped on a single line:
62+
* Now, the code in hit() maybe complicated but it does not matter: it follows the contract of the interface, and you can call it.
63+
* For example, you could create an object allowing you to hit a zombie with the following code:
64+
*
65+
* Monster zombie = ...; // Code getting the object for the zombie in the middle of the room
66+
* Weapon tossedWeapon = new Sword(); // or new Hammer() or new Axe() or new WhateverImplementsWeapon()
67+
* tossedWeapon.hit(zombie); // use the Weapon interface
68+
*
69+
* Notice the type of the variable 'tossedWeapon': it is a Weapon, not a Sword. Interfaces, like classes, are types you can use for declaring variables, fields, and parameters.
70+
* Because Sword implements Weapon, Java considers that a Sword object _is_ a Weapon. Having variables and parameters using the interface type allows you to work with any object implementing that interface.
3671
*
37-
* for([Initialize a counter]; [Condition on the counter variable]; [Modify the counter at the end of the loop]) {
38-
* // do stuff repetedly
72+
* Take a look at the bonuses.teachingmaterial.Combining interface. It defines a method which can be implemented in various ways.
73+
* This exercise is about implementing that interface in 2 ways.
74+
*
75+
* -------------------------------
76+
*
77+
* Expected result:
78+
*
79+
* The following code:
80+
*
81+
* Combining combining = new AddNumbers();
82+
* System.out.println(combining.combine(3, 4));
83+
* combining = new MultiplyNumbers();
84+
* System.out.println(combining.combine(3, 4));
85+
*
86+
* Should display:
87+
*
88+
* 7
89+
* 12
90+
*
91+
*/
92+
93+
94+
/**
95+
* # Anonymous interface implementation
96+
*
97+
* Write a method 'getAnonymousCombining' which returns an anonymous implementation of 'bonuses.teachingmaterial.Combining'.
98+
* The implementation of the combine() method should return the second number subtracted from the first.
99+
*
100+
* --------- TIPS --------------
101+
*
102+
* Sometimes, it is tedious to create a class file to implement an interface just for one occasion.
103+
* In these situation, you can implement the interface in an anonymous class. It is anonymous, because it does not have a name.
104+
* That class is instantiated immediately where it is created. For example:
105+
*
106+
* public Weapon toss() {
107+
* return new Weapon() {
108+
* @Override
109+
* public void hit(Monster monster) {
110+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc...
111+
* }
112+
* }
39113
* }
40114
*
41-
* Ex:
42-
*
43-
* for(int times = 3; times > 0; times = times -1) {
44-
* System.out.println("Still executing"); // Do stuff repetedly
115+
* When looking at this code, you could be tempted to believe there is a constructor for the interface Weapon, but there is not.
116+
* We are really creating a class, for which there exists a single object.
117+
* The constructor with empty parameters is the one of this nameless class.
118+
* We can now get and use the tossed weapon this way:
119+
*
120+
* Weapon tossedWeapon = toss();
121+
* tossedWeapon.hit(zombie);
122+
*
123+
* -------------------------------
124+
*
125+
* Expected result:
126+
*
127+
* getAnonymousCombining().combine(3, 4) should return -1
128+
*
129+
*/
130+
131+
132+
/**
133+
* # Lambda methods
134+
*
135+
* Write a method 'getLambdaCombining' which returns an lambda method implementing 'bonuses.teachingmaterial.Combining'.
136+
* The implementation of the combine() method should return the first number subtracted from the second.
137+
*
138+
* --------- TIPS --------------
139+
*
140+
* 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'.
141+
* 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:
142+
*
143+
* For example:
144+
*
145+
* public Weapon toss() {
146+
* return (monster) -> {
147+
* // Some code computing and applying damage to the monster, applying some tear and wear on the weapon, etc..
148+
* };
45149
* }
46150
*
47-
* There is even another shortcut: since incrementing or decrementing a number is something that happens very often, there is a short form:
151+
* We can now get and use the tossed weapon this way:
48152
*
49-
* a = a - 2; // Long form
50-
* a -= 2; // Short form
51-
* b = b + 2; // Long form
52-
* b += 2; // Short form
153+
* Weapon tossedWeapon = toss();
154+
* tossedWeapon.hit(zombie);
53155
*
54-
* When we increment / decrement by one, there is an even shorter form:
156+
* The general syntax for lambda method returning void, or having a body with multiple lines:
55157
*
56-
* a -= 1; // Short form
57-
* a--; // Shortest form
58-
* b += 1; // Short form
59-
* b++; // Shortest form
158+
* ([param1Name], [param2Name], ...) -> {
159+
* // Lambda method body here
160+
* }
161+
*
162+
* If your lambda is having a single expression, you can even skip the parentheses and the 'return':
163+
*
164+
* ([param1Name], [param2Name], ...) -> // expression here
165+
*
166+
* Here are some example of methods and their lambda equivalent (assuming the interface has only one method in it):
167+
*
168+
* This interface implementation:
169+
*
170+
* public void sayHello() {
171+
* System.out.println("hello");
172+
* }
173+
*
174+
* Can be replaced by this lambda:
175+
*
176+
* () -> {
177+
* System.out.println("hello");
178+
* }
179+
*
180+
* This interface implementation:
60181
*
61-
* Using this, our for loop can become even terser:
182+
* public int square(int x) {
183+
* return x * x;
184+
* }
185+
*
186+
* Can be replaced by this lambda:
187+
*
188+
* (x) -> x * x
62189
*
63-
* for(int times = 3; times > 0; times--) {
64-
* System.out.println("Still executing"); // Do stuff repetedly
190+
* This interface implementation:
191+
*
192+
* public int min(int x, int y) {
193+
* if (x < y) {
194+
* return x;
195+
* }
196+
* return y;
197+
* }
198+
*
199+
* Can be replaced by this lambda:
200+
*
201+
* (x, y) -> {
202+
* if (x < y) {
203+
* return x;
204+
* }
205+
* return y;
65206
* }
66207
*
67208
* -------------------------------
68209
*
69210
* Expected result:
70211
*
71-
* displayNumbers(3) should display:
212+
* getLambdaCombining().combine(3, 4) should return 1
213+
*
214+
*/
215+
216+
217+
/**
218+
* # Common lambda interfaces
219+
*
220+
* Write a method 'getIsEven' which returns a lambda method testing if an integer is even.
221+
*
222+
* --------- TIPS --------------
223+
*
224+
* 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.
225+
*
226+
* For example:
227+
*
228+
* For a lambda taking no parameter, and returning nothing, {@link java.lang.Runnable}:
229+
*
230+
* Runnable sayHello = () -> { System.out.println("Hello"); };
231+
*
232+
* For a lambda taking a int parameter, and returning nothing, {@link java.util.function.IntConsumer}:
233+
*
234+
* IntConsumer displayInt = (anInt) -> { System.out.println(anInt); };
235+
*
236+
* The same exist for other type. For example {@link java.util.function.DoubleConsumer}:
237+
*
238+
* DoubleConsumer displayDouble = (aDouble) -> { System.out.println(aDouble); };
239+
*
240+
* 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...
241+
*
242+
* DoubleSupplier giveMePiPleeeaaase = () -> 3.14159;
243+
*
244+
* 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:
245+
*
246+
* IntPredicate isPositive = (number) -> number >= 0;
247+
*
248+
* For the exercise, you can use the modulo operator, %, which computes the remainder of an integer division:
249+
*
250+
* int remainder = 17 % 5; // remainder equals 2
251+
*
252+
* -------------------------------
253+
*
254+
* Expected result:
72255
*
73-
* 1
74-
* 2
75-
* 3
256+
* getIsEven().test(4) should return true
76257
*
77258
*/
78259

src/main/java/engine/Printer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import java.util.Arrays;
55
import java.util.List;
66

7-
import engine.test.Line;
7+
import engine.test.runner.Line;
88

99
/**
1010
* A Printer allows to print feedback to the student.

src/main/java/engine/TestSensei.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import java.util.stream.IntStream;
55

66
import engine.script.ScriptRunner;
7-
import engine.test.Line;
7+
import engine.test.runner.Line;
88

9+
/**
10+
* A sensei used to run unit tests for the FRC Java Koans, for contributors.
11+
*/
912
public class TestSensei {
1013
public static final Locale TEST_LOCALE = Locale.en;
1114

src/main/java/engine/Texts.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,14 @@ public class Texts {
217217
local("Expected %s to return %d from random number %f but returned %d instead!")
218218
.fr("Attendu à ce que %s retourne %d à partir du nombre aléatoire %f mais a retourné %d à la place!");
219219
public static Localizable<String> OK_RETURNED_INT_FROM_RANDOM =
220-
local("Ok: %s returned %d from random number %f.")
221-
.fr("Ok: %s a retourné %d à partir du nombre aléatoire %f.");
220+
local("Ok: %s returned %s from random number %f.")
221+
.fr("Ok: %s a retourné %s à partir du nombre aléatoire %f.");
222222
public static Localizable<String> EXPECTED_TO_RETURN_INT_FROM_RANDOMS_BUT_RETURNED =
223-
local("Expected %s to return %d from random numbers %s but returned %d instead!")
224-
.fr("Attendu à ce que %s retourne %d à partir des nombres aléatoires %s mais a retourné %d à la place!");
223+
local("Expected %s to return %s from random numbers %s but returned %d instead!")
224+
.fr("Attendu à ce que %s retourne %s à partir des nombres aléatoires %s mais a retourné %d à la place!");
225225
public static Localizable<String> OK_RETURNED_INT_FROM_RANDOMS =
226-
local("Ok: %s returned %d from random numbers %s.")
227-
.fr("Ok: %s a retourné %d à partir du nombre aléatoire %s.");
226+
local("Ok: %s returned %s from random numbers %s.")
227+
.fr("Ok: %s a retourné %s à partir du nombre aléatoire %s.");
228228
public static Localizable<String> EXPECTED_FIELD_TO_BE_PRIVATE =
229229
local("Expected '%s' field in class %s to be private, but it is not.")
230230
.fr("Attendu à ce que le champ '%s' dans la classe %s soit privé, mais il ne l'est pas.");

0 commit comments

Comments
 (0)