From e937a27ff2c2e10442f3f9dc13276cfcb96aff97 Mon Sep 17 00:00:00 2001 From: smcg468 <49883535+smcg468@users.noreply.github.com> Date: Sat, 25 Nov 2023 21:39:09 +0000 Subject: [PATCH] Add nullability concept (#2584) * Nullability concept added * Updating error message * Updating config.json * Linting issues and prerequisites updated in config.json * Addressing review comments * Removing trailing space Fixes #2553 --- concepts/nullability/.meta/config.json | 7 +++ concepts/nullability/about.md | 53 +++++++++++++++++++ concepts/nullability/introduction.md | 24 +++++++++ concepts/nullability/links.json | 22 ++++++++ config.json | 17 ++++++ .../concept/tim-from-marketing/.docs/hints.md | 20 +++++++ .../tim-from-marketing/.docs/instructions.md | 47 ++++++++++++++++ .../tim-from-marketing/.docs/introduction.md | 26 +++++++++ .../.docs/introduction.md.tpl | 5 ++ .../tim-from-marketing/.meta/config.json | 23 ++++++++ .../tim-from-marketing/.meta/design.md | 20 +++++++ .../.meta/src/reference/java/Badge.java | 20 +++++++ .../concept/tim-from-marketing/build.gradle | 23 ++++++++ .../src/main/java/Badge.java | 5 ++ .../src/test/java/BadgeTest.java | 40 ++++++++++++++ exercises/settings.gradle | 2 + 16 files changed, 354 insertions(+) create mode 100644 concepts/nullability/.meta/config.json create mode 100644 concepts/nullability/about.md create mode 100644 concepts/nullability/introduction.md create mode 100644 concepts/nullability/links.json create mode 100644 exercises/concept/tim-from-marketing/.docs/hints.md create mode 100644 exercises/concept/tim-from-marketing/.docs/instructions.md create mode 100644 exercises/concept/tim-from-marketing/.docs/introduction.md create mode 100644 exercises/concept/tim-from-marketing/.docs/introduction.md.tpl create mode 100644 exercises/concept/tim-from-marketing/.meta/config.json create mode 100644 exercises/concept/tim-from-marketing/.meta/design.md create mode 100644 exercises/concept/tim-from-marketing/.meta/src/reference/java/Badge.java create mode 100644 exercises/concept/tim-from-marketing/build.gradle create mode 100644 exercises/concept/tim-from-marketing/src/main/java/Badge.java create mode 100644 exercises/concept/tim-from-marketing/src/test/java/BadgeTest.java diff --git a/concepts/nullability/.meta/config.json b/concepts/nullability/.meta/config.json new file mode 100644 index 000000000..cbd2f43a4 --- /dev/null +++ b/concepts/nullability/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "In Java, the null literal is used to denote the absence of a value.", + "authors": [ + "smcg468" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/nullability/about.md b/concepts/nullability/about.md new file mode 100644 index 000000000..88bfd3255 --- /dev/null +++ b/concepts/nullability/about.md @@ -0,0 +1,53 @@ +# About + +In Java, the [`null` literal][null-keyword] is used to denote the absence of a value. + +[Primitive variables][primitive-data-types] in java all have a default value and therefore can never be `null`. +By convention, they start with a lowercase letter e.g `int` + +[Reference variables][reference-data-types] contain the memory address of an object and can have a value of null. +These variables usually start with an uppercase e.g `String` + +Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds +a default value of the type assigned. + +```java +//Throws compile time error stating the required type is int, but null was provided +int number = null; +``` + +Assigning a reference variable a `null` value will not result in a compile time error as reference variables are nullable. + +```java +//No error will occur as the String variable str is nullable +String str = null; +``` + +Whilst accessing a reference variable which has a value of `null` will compile fine, it will result in a `NullPointerException` being thrown at runtime. + +```java +int[] arr = null; + +// Throws NullPointerException at runtime +arr.Length; +``` + +A [`NullPointerException` is thrown][null-pointer-exception] when trying to access a reference variable which is null but requires an object. + +To safely work with nullable values, one should check if they are `null` before working with them which can be done using [equality operators][equality-operators] such as `==` or `!=`: + +```java +int[] arr = null; + +if(arr != null) { + System.out.println(arr.length); +} else { + //Perform an alternate action when arr is null +} +``` + +[null-keyword]: https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.7 +[primitive-data-types]: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html +[reference-data-types]: https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.3 +[null-pointer-exception]: https://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html +[equality-operators]: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html diff --git a/concepts/nullability/introduction.md b/concepts/nullability/introduction.md new file mode 100644 index 000000000..cf59dc1d8 --- /dev/null +++ b/concepts/nullability/introduction.md @@ -0,0 +1,24 @@ +# Introduction + +In Java, the `null` literal is used to denote the absence of a value. + +Primitive data types in java all have a default value and therefore can never be `null`. +By convention, they start with a lowercase letter e.g `int` + +Reference types contain the memory address of an object can have a value of null. +These variables usually start with an uppercase e.g `String` + +Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds +a primitive value of the type assigned. + +```java +//Throws compile time error stating the required type is int, but null was provided +int number = null; +``` + +Assigning a reference variable a `null` value will not result in a compile time error as reference variables are nullable. + +```java +//No error will occur as the String variable str is nullable +String str = null; +``` diff --git a/concepts/nullability/links.json b/concepts/nullability/links.json new file mode 100644 index 000000000..65761f5c3 --- /dev/null +++ b/concepts/nullability/links.json @@ -0,0 +1,22 @@ +[ + { + "url": "https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.7", + "description": "null-keyword" + }, + { + "url": "https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html", + "description": "primitive-data-types" + }, + { + "url": "https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.3", + "description": "reference-data-types" + }, + { + "url": "https://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html", + "description": "null-pointer-exception" + }, + { + "url": "https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html", + "description": "equality-operators" + } +] \ No newline at end of file diff --git a/config.json b/config.json index ab2f0f901..a8a55ae6d 100644 --- a/config.json +++ b/config.json @@ -234,6 +234,18 @@ "switch-statement", "constructors" ] + }, + { + "slug": "tim-from-marketing", + "name": "Tim from Marketing", + "uuid": "28bd20c5-4fdd-4660-9225-54f24aae24e4", + "concepts": [ + "nullability" + ], + "prerequisites": [ + "if-else-statements", + "strings" + ] } ], "practice": [ @@ -2088,6 +2100,11 @@ "slug": "lists", "name": "Lists" }, + { + "uuid": "0718bff1-25ad-42bb-860d-1b0834beb9fc", + "slug": "nullability", + "name": "Nullability" + }, { "uuid": "58529dab-0ef2-4943-ac12-a98ca79b922b", "slug": "numbers", diff --git a/exercises/concept/tim-from-marketing/.docs/hints.md b/exercises/concept/tim-from-marketing/.docs/hints.md new file mode 100644 index 000000000..0faf9ca6e --- /dev/null +++ b/exercises/concept/tim-from-marketing/.docs/hints.md @@ -0,0 +1,20 @@ +# Hints + +## 1. Print a badge for an employee + +- [String interpolation][string-interpolation] can be used to create description strings. +- There is a [built-in method to convert a string to uppercase][to-upper-case]. + +## 2. Print a badge for a new employee + +- You should check if the ID is `null` before using it. +- You can use the [equality operators][equality-operators] to compare the value to `null` + +## 3. Print a badge for the owner + +- You should check if the department is `null` before using it. +- You can use the [equality operators][equality-operators] to compare the value to `null` + +[string-interpolation]: https://www.baeldung.com/java-string-interpolation +[to-upper-case]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#toUpperCase-- +[equality-operators]: https://docs.oracle.com/cd/E21764_01/apirefs.1111/e17787/com/sigmadynamics/util/Null.html#isNull_java_lang_String_ diff --git a/exercises/concept/tim-from-marketing/.docs/instructions.md b/exercises/concept/tim-from-marketing/.docs/instructions.md new file mode 100644 index 000000000..ea2fae9ba --- /dev/null +++ b/exercises/concept/tim-from-marketing/.docs/instructions.md @@ -0,0 +1,47 @@ +# Instructions + +In this exercise you'll be writing code to print name badges for factory employees. + +## 1. Print a badge for an employee + +Employees have an ID, name and department name. Employee badge labels are formatted as follows: `"[id] - name - DEPARTMENT"`. +Implement the `Badge.print()` method to return an employee's badge label: + +```java +Badge badge = new Badge(); +badge.print(734, "Ernest Johnny Payne", "Strategic Communication"); +// => "[734] - Ernest Johnny Payne - STRATEGIC COMMUNICATION" +``` + +Note that the department should be uppercased on the label. + +## 2. Print a badge for a new employee + +Due to a quirk in the computer system, new employees occasionally don't yet have an ID when they start working at the factory. +As badges are required, they will receive a temporary badge without the ID prefix. Modify the `Badge.print()` method to support new employees that don't yet have an ID: + +```java +Badge badge = new Badge(); +Badge.print(null, "Jane Johnson", "Procurement"); +// => "Jane Johnson - PROCUREMENT" +``` + +## 3. Print a badge for the owner + +Even the factory's owner has to wear a badge at all times. +However, an owner does not have a department. In this case, the label should print `"OWNER"` instead of the department name. +Modify the `Badge.print()` method to print a label for the owner: + +```java +Badge badge = new Badge(); +badge.print(254, "Charlotte Hale", null); +// => "[254] - Charlotte Hale - OWNER" +``` + +Note that it is possible for the owner to also be a new employee: + +```java +Badge badge = new Badge(); +badge.print(null, "Charlotte Hale", null); +// => "Charlotte Hale - OWNER" +``` diff --git a/exercises/concept/tim-from-marketing/.docs/introduction.md b/exercises/concept/tim-from-marketing/.docs/introduction.md new file mode 100644 index 000000000..87a92e293 --- /dev/null +++ b/exercises/concept/tim-from-marketing/.docs/introduction.md @@ -0,0 +1,26 @@ +# Introduction + +## Nullability + +In Java, the `null` literal is used to denote the absence of a value. + +Primitive data types in java all have a default value and therefore can never be `null`. +By convention, they start with a lowercase letter e.g `int` + +Reference types contain the memory address of an object can +have a value of null. These variables usually start with an uppercase e.g `String` + +Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds +a primitive value of the type assigned. + +```java +//Throws compile time error stating the required type is int, but null was provided +int number = null; +``` + +Assigning a reference variable a `null` value will not result in a compile time error as reference variables are nullable. + +```java +//No error will occur as the String variable str is nullable +String str = null; +``` diff --git a/exercises/concept/tim-from-marketing/.docs/introduction.md.tpl b/exercises/concept/tim-from-marketing/.docs/introduction.md.tpl new file mode 100644 index 000000000..ea9499a77 --- /dev/null +++ b/exercises/concept/tim-from-marketing/.docs/introduction.md.tpl @@ -0,0 +1,5 @@ +# Introduction + +## Nullability + +%{concept:nullability} \ No newline at end of file diff --git a/exercises/concept/tim-from-marketing/.meta/config.json b/exercises/concept/tim-from-marketing/.meta/config.json new file mode 100644 index 000000000..c1b8f0268 --- /dev/null +++ b/exercises/concept/tim-from-marketing/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "smcg468" + ], + "files": { + "solution": [ + "src/main/java/Badge.java" + ], + "test": [ + "src/test/java/BadgeTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Badge.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/tim-from-marketing" + ], + "blurb": "Learn about the null literal and nullable variables in java by printing name badges." +} diff --git a/exercises/concept/tim-from-marketing/.meta/design.md b/exercises/concept/tim-from-marketing/.meta/design.md new file mode 100644 index 000000000..4fba63130 --- /dev/null +++ b/exercises/concept/tim-from-marketing/.meta/design.md @@ -0,0 +1,20 @@ +# Design + +## Learning objectives + +- Know of the existence of the `null` literal. +- Know what a `NullPointerException` is and when it is thrown. +- Know how to compare a value to `null`. + +## Out of scope + +- `java.util.Optional` + +## Concepts + +- `nullability` + +## Prerequisites + +- `strings`: strings will be compared to null and basic methods from strings will be called. +- `if-else-statements`: using a conditional statement. diff --git a/exercises/concept/tim-from-marketing/.meta/src/reference/java/Badge.java b/exercises/concept/tim-from-marketing/.meta/src/reference/java/Badge.java new file mode 100644 index 000000000..7299a697f --- /dev/null +++ b/exercises/concept/tim-from-marketing/.meta/src/reference/java/Badge.java @@ -0,0 +1,20 @@ +public class Badge { + + public String print(Integer id, String name, String department) { + + String worksAt; + + if (department == null) { + worksAt = "OWNER"; + } else { + worksAt = department.toUpperCase(); + } + + if (id == null) { + return name + " - " + worksAt; + } + + return "[" + id + "] - " + name + " - " + worksAt; + } + +} diff --git a/exercises/concept/tim-from-marketing/build.gradle b/exercises/concept/tim-from-marketing/build.gradle new file mode 100644 index 000000000..381f38afc --- /dev/null +++ b/exercises/concept/tim-from-marketing/build.gradle @@ -0,0 +1,23 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.15.0" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} \ No newline at end of file diff --git a/exercises/concept/tim-from-marketing/src/main/java/Badge.java b/exercises/concept/tim-from-marketing/src/main/java/Badge.java new file mode 100644 index 000000000..44b5d4389 --- /dev/null +++ b/exercises/concept/tim-from-marketing/src/main/java/Badge.java @@ -0,0 +1,5 @@ +class Badge { + public String print(Integer id, String name, String department) { + throw new UnsupportedOperationException("Please implement the (static) Badge.print() method"); + } +} diff --git a/exercises/concept/tim-from-marketing/src/test/java/BadgeTest.java b/exercises/concept/tim-from-marketing/src/test/java/BadgeTest.java new file mode 100644 index 000000000..929a9fbe6 --- /dev/null +++ b/exercises/concept/tim-from-marketing/src/test/java/BadgeTest.java @@ -0,0 +1,40 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +public class BadgeTest { + + @Test + @Tag("task:1") + @DisplayName("Printing a badge for an employee") + public void labelForEmployee() { + Badge badge = new Badge(); + assertThat(badge.print(17, "Ryder Herbert", "Marketing")) + .isEqualTo("[17] - Ryder Herbert - MARKETING"); + } + + @Test + @Tag("task:2") + @DisplayName("Printing a badge for a new employee") + public void labelForNewEmployee() { + Badge badge = new Badge(); + assertThat(badge.print(null, "Bogdan Rosario", "Marketing")).isEqualTo("Bogdan Rosario - MARKETING"); + } + + @Test + @Tag("task:3") + @DisplayName("Printing a badge for the owner") + public void labelForOwner() { + Badge badge = new Badge(); + assertThat(badge.print(59, "Julie Sokato", null)).isEqualTo("[59] - Julie Sokato - OWNER"); + } + + @Test + @Tag("task:3") + @DisplayName("Printing a badge for the owner who is a new employee") + public void labelForNewOwner() { + Badge badge = new Badge(); + assertThat(badge.print(null, "Amare Osei", null)).isEqualTo("Amare Osei - OWNER"); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 2c541552e..452db9b5f 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -17,6 +17,7 @@ include 'concept:football-match-reports' include 'concept:wizards-and-warriors' include 'concept:calculator-conundrum' include 'concept:logs-logs-logs' +include 'concept:tim-from-marketing' // practice exercises include 'practice:accumulate' @@ -150,3 +151,4 @@ include 'practice:wordy' include 'practice:yacht' include 'practice:zebra-puzzle' include 'practice:zipper' +