diff --git a/README.md b/README.md index 5c15e22..5690a74 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,11 @@ first time submitting a PR to an open-source project. Head over to the [Wiki](https://github.com/Together-Java/ModernJava/wiki) as general entry point to the project. It provides lots of tutorials, documentation and other information. + +To run this locally you will need `mdbook`. The instructions for doing so are [here](https://rust-lang.github.io/mdBook/guide/installation.html). + +Then just run the following. + +``` +mdbook serve +``` diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d86c825..4c6fa5f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -138,6 +138,10 @@ - [Initialization with new](./arrays/initialization_with_new.md) - [Challenges](./arrays/challenges.md) +# Projects + +- [The Boston Molasses Disaster Game]() + # Control Flow II - [Loops II](./loops_ii.md) @@ -172,7 +176,6 @@ - [Challenges](./methods/challenges.md) - [Arguments](./arguments.md) - - [Declaration](./arguments/declaration.md) - [Invocation with Arguments](./arguments/invocation_with_arguments.md) - [Reassignment](./arguments/reassignment.md) @@ -192,6 +195,10 @@ - [Unreachable Statements](./return_values/unreachable_statements.md) - [Challenges](./return_values/challenges.md) +# Projects + +- [Tic-Tac-Toe]() + # Data Types III - [null](./null.md) @@ -215,6 +222,11 @@ - [Populate Arrays](./arrays_ii/populate_arrays.md) - [Challenges](./arrays_ii/challenges.md) +# Projects II + + +- [Ultimate Tic-Tac-Toe]() + # Code Structure II - [Classes](./classes.md) @@ -409,7 +421,7 @@ - [Conventions](./command_line_arguments/conventions.md) -# Code Structure IV +# Code Structure V - [Inner Classes](./inner_classes.md) - [Type](./inner_classes/type.md) @@ -475,20 +487,10 @@ +--> -# Code Structure IV +# Code Structure VI - [Object](./objects.md) - [Subtypes](./objects/subtypes.md) @@ -529,6 +531,8 @@ Make them do one. --> - [Time Zones](./time/time_zones.md) - [ZonedDateTime](./time/zoned_date_time.md) - [OffsetDateTime](./time/offset_date_time.md) + - [Date](./time/date.md) + - [Challenges](./time/challenges.md) - [ArrayList](./array_list.md) - [Ubiquity](./array_list/ubiquity.md) - [Add an item](./array_list/add_an_item.md) @@ -537,37 +541,35 @@ Make them do one. --> - [Loop over items](./array_list/loop_over_items.md) - [Set an item](./array_list/set_an_item.md) - [Remove an item](./array_list/remove_an_item.md) - # Concepts II - [Hyrum's Law](./hyrums_law.md) - [Authority](./hyrums_law/authority.md) - [Validity](./hyrums_law/validity.md) - [Emergent Properties](./hyrums_law/emergent_properties.md) - [Importance](./hyrums_law/importance.md) -- [Encapsulation](./encapsulation.md) - - [Implementation Details](./encapsulation/implementation_details.md) - - [Methods](./encapsulation/methods.md) - - [Classes]()(./encapsulation/classes.md) - - [Implicit Interfaces](./encapsulation/implicit_interfaces.md) - - [Abstractions](./encapsulation/abstractions.md) - - [Leaky Abstractions](./encapsulation/leaky_abstractions.md) - - [Information Hiding](./encapsulation/information_hiding.md) # Control Flow V -- [Loops III](./loops_iii.md) - - [For-each loops](./loops_iii/for_each_loops.md) - - [Arrays]() - - [Iterable]() - - [ArrayList]() - - [Concurrent Modifications](./loops_iii/concurrent_modifications.md) - - [Reasons to go back]() - [Recurse Over a String](./recursion/recursing_over_strings.md) - [Recurse Over an Array](./recursion/recursing_over_arrays.md) +- [Loops III](./loops_iii.md) + - [For-each loops](./loops_iii/for_each_loops.md) + - [Arrays](./loops_iii/arrays.md) + - [Iterable and Iterator](./loops_iii/iterable_and_iterator.md) + - [ArrayList](./loops_iii/arraylist.md) + - [String](./loops_iii/string.md) + - [Concurrent Modifications](./loops_iii/concurrent_modifications.md) + - [Inferred Types](./loops_iii/inferred_types.md) + +# Concepts II + +- [Encapsulation](./encapsulation.md) + - [Implementation Details](./encapsulation/implementation_details.md) + - [Implicit Interfaces](./encapsulation/implicit_interfaces.md) + - [Methods](./encapsulation/methods.md) + - [Classes](./encapsulation/classes.md) + - [Abstraction](./encapsulation/abstractions.md) + - [Coupling](./encapsulation/coupling.md) + - [Leaky Abstractions](./encapsulation/leaky_abstractions.md) + - [Information Hiding](./encapsulation/information_hiding.md) + + +# Data Types VI + +- [Collections](./collections.md) + - [List](./collections/list.md) + - [Map](./collections/map.md) + - [Set](./collections/set.md) + - [Arrays](./collections/arrays.md) + - [UnsupportedOperationException](./collections/unsupported_operation_exception.md) + - [Factories](./collections/factories.md) + - [Specificity](./collections/specificity.md) + + +- [Multi-Dimensional Arrays](./multi_dimensional_arrays.md) + - [Declaration](./multi_dimensional_arrays/declaration.md) + - [Array Initializers](./multi_dimensional_arrays/array_initializers.md) + - [Initialization with new](./multi_dimensional_arrays/initialization_with_new.md) + - [Access Individual Elements](./multi_dimensional_arrays/access_individual_elements.md) + - [Set Individual Elements](./multi_dimensional_arrays/set_individual_elements.md) + - [Initialization with Size](./multi_dimensional_arrays/initialize_with_size.md) + - [Default Values](./multi_dimensional_arrays/default_values.md) + - [Populate Values](./multi_dimensional_arrays/populate_values.md) + - [Ragged Arrays](./multi_dimensional_arrays/ragged_arrays.md) + +# Metaprogramming + +- [Reflection](./reflection.md) + - [Class Objects](./reflection/class_objects.md) + - [Get all Fields](./reflection/get_all_fields.md) + - [Get a Field](./reflection/get_a_field.md) + - [Read from a Field](./reflection/read_from_a_field.md) + - [Write to a Field](./reflection/write_to_a_field.md) + - [Get all Methods](./reflection/get_all_methods.md) + - [Get a Method](./reflection/get_a_method.md) + - [Invoke a Method](./reflection/invoke_a_method.md) + - [Get a Constructor](./reflection/get_a_constructor.md) + - [Get all Constructors](./reflection/get_all_constructors.md) + - [Invoke a Constructor](./reflection/invoke_a_constructor.md) + +- [Annotations](./annotations.md) + - [Declaration](./annotations/declaration.md) + - [Usage](./annotations/usage.md) + - [Elements](./annotations/elements.md) + - [Usage with Elements](./annotations/usage_with_elements.md) + - [Defaults](./annotations/defaults.md) + - [@Target](./annotations/target.md) + - [@Retention](./annotations/retention.md) + - [Reflective Access](./annotations/reflective_access.md) + - [@Override](./annotations/override.md) + + + + +# Code Structure VII +- [Interfaces II](./interfaces_ii.md) + - [Default Methods](./interfaces_ii/default_methods.md) + - [Interface Extension](./interfaces_ii/interface_extension.md) + - [Static Methods](./interfaces_ii/static_methods.md) + - [Static Fields](./interfaces_ii/static_fields.md) +- [Class Extension](./class_extension.md) + - [Extend a Class](./class_extension/extend_a_class.md) + - [Inheritance](./class_extension/inheritance.md) + - [Override](./class_extension/override.md) + - [Protected](./class_extension/protected.md) + - [Abstract Classes](./class_extension/abstract_classes.md) + - [Abstract Methods](./class_extension/abstract_methods.md) + - [Relation to Interfaces](./class_extension/relation_to_interfaces.md) + - [Relation to Encapsulation](./class_extension/relation_to_encapsulation.md) + - [Final Classes](./class_extension/final_classes.md) + + +# Data Types VII + +- [Niche Numerics](./niche_numerics.md) + - [byte](./niche_numerics/byte.md) + - [short](./niche_numerics/short.md) + - [long](./niche_numerics/long.md) + - [Unsigned Operations](./niche_numerics/unsigned_operations.md) + - [float](./niche_numerics/float.md) + +# Code Structure VIII + +- [Modules](./modules.md) + - [Declaration](./modules/declaration.md) + - [Restrictions](./modules/restrictions.md) + - [Exports]() + - [Integrity]() + - [java.base](./modules/java.base.md) + - [The Unnamed Module](./modules/the_unnamed_module.md) + - [Module Imports](./modules/module_imports.md) + - [Multi-Module Directory Layout]() +- [Lambdas](./lambdas.md) + - [Functional Interfaces](./lambdas/functional_interfaces.md) + - [@FunctionalInterface](./lambdas/functional_interface_annotation.md) + - [Lambda Expressions](./lambdas/lambda_expressions.md) + - [Method References](./lambdas/method_references.md) + - [Runnable]() + - [Function]() + - [Relation to Checked Exceptions]() +- [Streams](./streams.md) + - [map](./streams/map.md) + - [filter](./streams/filter.md) + - [Collectors](./streams/collectors.md) + - [toList](./streams/toList.md) + - [mapMulti](./streams/mapMulti.md) + - [Gatherers](./streams/gatherers.md) + +# Sharing Code + +- [Compilation](./compilation.md) + - [javac]() + - [--release]() +- [Packaging]() + - [jar]() + - [--main-class]() +- [Documentation]() + - [javadoc]() + - [Documentation Comments](./documentation/documentation_comments.md) +- [Distribution]() + - [jlink]() + + - [Low Level versus High Level](./tcp_over_ip/low_level_versus_high_level.md) - [Data Formats]() -# Metaprogramming - -- [Reflection](./reflection.md) - - [Class Objects](./reflection/class_objects.md) - - [Get all Fields](./reflection/get_all_fields.md) - - [Get a Field]() - - [Set a Field]() - - [Get a Method]() - - [Call a Method]() - - [Get a Constructor]() - -- [Annotations]() - -# Concepts III -- [Acronyms](./acronyms.md) - - [Niches](./acronyms/niches.md) - - [Usage Contexts](./acronyms/usage_contexts.md) - - [Ambiguity](./acronyms/ambiguity.md) - - [Familiarity](./acronyms/familiarity.md) - - [Elaboration](./acronyms/elaboration.md) # Code Structure VI @@ -646,6 +797,7 @@ Make them do one. --> - [Pattern Matching]() - [Primitive Patterns]() - [Record Patterns]() + --> - [Integers II]() - [Paths]() -- [Iterator and Iterable]() - [Exceptions II]() # Control Flow IV -- [Loops III](./loops_iii.md) - - [For Each]() - # Algorithms II -- [Hash Map]() - # Data Types VII - [Maps]() @@ -720,11 +866,9 @@ Generics Maven XML Pom -byte, short, long Multiversal Equality Reflection Cover Invoking *public* constructs -Annotations Javadoc and Documentation comments --> @@ -834,11 +978,8 @@ jshell ``` - Getting Started -- [Hello, World]() - Data Types - - Arrays -multi dimensional arrays - Expressions - Conditionals - Loops @@ -872,7 +1013,20 @@ multi dimensional arrays - Maven before hikaricp - HikariCP for connection pool -System.console() over Scanner? +The You of today is not the you of tommorow + + +Separate a mobile number given in an international format into parts countrycode, number +I think its a nice example about a problem which can be solved in a number of ways, and still being very precise and compact in description +mikamoilanen — 3:53 PM +Start with a list of few countrycodes, like +1, +358 +Given a phone number eg "+358451252855", separate it into two parts: +358 and 451252855 +The essiest way is to sort country codes by lenght, longest first, and loop through them by asking if the phonenumber.startsWith(cc) +(this works as codes are common-prefix-free) +mikamoilanen — 4:01 PM +Another solution creates a nice opportunity to introduce creating your own datastructure for more efficient solution: Build a tree out of the codes, where each number has N childs, and traverse throught that tree number-by-number of the phonenumber +THEN you could even introduce packing the data as bitvectors in order to have even more compact data structure + --> diff --git a/src/abbreviations.md b/src/abbreviations.md new file mode 100644 index 0000000..4c8846a --- /dev/null +++ b/src/abbreviations.md @@ -0,0 +1,14 @@ +# Abbreviations + +In the 1940s Franklin Delano Rosevelt (FDR) founded the AAA, CCC, FDIC, NRA, and many more "[Alphabet Agencies](https://en.wikipedia.org/wiki/Alphabet_agencies)." + +People called them the alphabet agencies because there were so many and their + + as part of an effort to lift the United States out of the [Great Depression](https://en.wikipedia.org/wiki/Great_Depression). + +NASA is an acronym + +FDIC is an initialism + +[^mrdoyle]: One of my high school history teachers, who loved FDR and named his kid after him, is banned from a at least one local museum due to an incident. + diff --git a/src/acronyms/ambiguity.md b/src/abbreviations/ambiguity.md similarity index 100% rename from src/acronyms/ambiguity.md rename to src/abbreviations/ambiguity.md diff --git a/src/acronyms/elaboration.md b/src/abbreviations/elaboration.md similarity index 100% rename from src/acronyms/elaboration.md rename to src/abbreviations/elaboration.md diff --git a/src/acronyms/familiarity.md b/src/abbreviations/familiarity.md similarity index 100% rename from src/acronyms/familiarity.md rename to src/abbreviations/familiarity.md diff --git a/src/acronyms/niches.md b/src/abbreviations/niches.md similarity index 90% rename from src/acronyms/niches.md rename to src/abbreviations/niches.md index 60b4487..6924136 100644 --- a/src/acronyms/niches.md +++ b/src/abbreviations/niches.md @@ -1 +1,2 @@ # Niches + diff --git a/src/acronyms/usage_contexts.md b/src/abbreviations/usage_contexts.md similarity index 100% rename from src/acronyms/usage_contexts.md rename to src/abbreviations/usage_contexts.md diff --git a/src/abstract_classes.md b/src/abstract_classes.md deleted file mode 100644 index 6a4037d..0000000 --- a/src/abstract_classes.md +++ /dev/null @@ -1 +0,0 @@ -# Abstract Classes diff --git a/src/acronyms.md b/src/acronyms.md deleted file mode 100644 index 802eb0a..0000000 --- a/src/acronyms.md +++ /dev/null @@ -1,3 +0,0 @@ -# Acronyms - -In the 1940s, Franklin Delano Rosevelt \ No newline at end of file diff --git a/src/annotations.md b/src/annotations.md new file mode 100644 index 0000000..4b760bb --- /dev/null +++ b/src/annotations.md @@ -0,0 +1,57 @@ +# Annotations + +Comments are useful when reading code. Since they can contain any text, +you can use them to clarify your intent, make note of issues to address +later, etc. + +```java +class Main { + // TODO: Make this actually add one + int addOne(int x) { + return x + 2; + } + + // The return value here will always be non-negative + int absoluteValue(int x) { + if (x < 0) { + return -x; + } + else { + return x; + } + } + + int main() { + IO.println(addOne(5)); + IO.println(absoluteValue(-25)); + } +} +``` + +The problem with comments is that they are just text. It would be difficult to write a program +that acts upon or uses information in a comment. + +Annotations, which you can think of as "structured comments", are useful for this purpose. + +```java +class Main { + @TODO("Make this actually add one") + int addOne(int x) { + return x + 2; + } + + @NonNegative int absoluteValue(int x) { + if (x < 0) { + return -x; + } + else { + return x; + } + } + + int main() { + IO.println(addOne(5)); + IO.println(absoluteValue(-25)); + } +} +``` \ No newline at end of file diff --git a/src/annotations/declaration.md b/src/annotations/declaration.md new file mode 100644 index 0000000..d276869 --- /dev/null +++ b/src/annotations/declaration.md @@ -0,0 +1,12 @@ +# Declaration + +To declare an annotation you use `@interface` followed by a type name. + +```java,no_run +@interface NonNegative { +} +``` + +We call these "annotation interfaces."[^shared] + +[^shared]: They share some properties with regular interfaces but it's best to think of them as their own thing. \ No newline at end of file diff --git a/src/annotations/defaults.md b/src/annotations/defaults.md new file mode 100644 index 0000000..33ccf94 --- /dev/null +++ b/src/annotations/defaults.md @@ -0,0 +1,63 @@ +# Defaults + +You can give a default value for an element by writing `default` followed by a value. + +You don't need to explicitly specify a value for any elements that have a default. + +```java +enum Priority { + HIGH, + LOW +} + +@interface Todo { + String description(); + Priority priority() default Priority.LOW; +} + +// Priority does not need to be specified since +// it has a default specified +@Todo(description="Write the code") +class Code { + // But you can still specify it in situations where you want to + @Todo(description="Write main method", priority=Priority.HIGH) + void main() { + } +} +``` + +If all values have defaults then you don't need to specify anything. + +```java +enum Priority { + HIGH, + LOW +} + +@interface Todo { + String description() default "Do Stuff"; + Priority priority() default Priority.LOW; +} + +@Todo +class Code { +} +``` + +And if the only element that doesn't have a default value is named `value`, and thats the only one you want to specify, you don't need to give its name. + +```java +enum Priority { + HIGH, + LOW +} + +@interface Todo { + String value(); + Priority priority() default Priority.LOW; +} + +@Todo("Really need to write something in here") +class Code { +} +``` \ No newline at end of file diff --git a/src/annotations/deprecated b/src/annotations/deprecated new file mode 100644 index 0000000..d57b74b --- /dev/null +++ b/src/annotations/deprecated @@ -0,0 +1 @@ +# @Deprecated diff --git a/src/annotations/elements.md b/src/annotations/elements.md new file mode 100644 index 0000000..c7891ba --- /dev/null +++ b/src/annotations/elements.md @@ -0,0 +1,57 @@ +# Elements + +Annotation interfaces can contain any number of elements. + +```java,no_run +@interface Todo { + String description(); +} +``` + +These are declared in the same way as methods in an interface, save for some +important restrictions. + +The method should take no arguments. + +```java,no_run,does_not_compile +@interface Todo { + // Cannot take arguments + String description(int x); +} +``` + +And the return value needs to be one of a few built-in types like `Class`, `String`, `int`, `boolean`, etc.[^fulllist], an `enum`, or an array of one of those. + +```java,no_run,does_not_compile +enum Priority { + HIGH, + LOW +} + +class PersonInCharge {} + +@interface Todo { + // String ✅ + String description(); + + // int ✅ + int someNumber(); + + // boolean ✅ + boolean isImportant(); + + // Class ✅ + Class someRelatedClass(); + + // Arrays ✅ + String[] notes(); + + // Enums ✅ + Priority priority(); + + // Other Classes ❌ + PersonInCharge personInCharge(); +} +``` + +[^fulllist]: You can find the full list [here](https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html#jls-9.6). It is a short list. diff --git a/src/annotations/override.md b/src/annotations/override.md new file mode 100644 index 0000000..ba78a44 --- /dev/null +++ b/src/annotations/override.md @@ -0,0 +1,33 @@ +# @Override + +The `@Override` you can put on methods to have Java check that you are properly overriding them +is an annotation. + +There's not much else to say besides "hey, thats what that was!" but I think its +worth noting. + +```java +interface Num { + boolean odd(); + + boolean even(); +} + +class IntNum implements Num { + final int x; + + IntNum(int x) { + this.x = x; + } + + @Override // This is an annotation? + public boolean odd() { + return x % 2 != 0; + } + + @Override // Always has been. + public boolean even() { + return !odd(); + } +} +``` \ No newline at end of file diff --git a/src/annotations/reflective_access.md b/src/annotations/reflective_access.md new file mode 100644 index 0000000..f889f62 --- /dev/null +++ b/src/annotations/reflective_access.md @@ -0,0 +1,56 @@ +# Reflective Access + +If something is annotated with a runtime retained annotation, +you can get an object holding that data using `.getAnnotation`. + +This will either return `null` if the field, method, class, etc. is not +annotated with that annotation or it will give you an object +with methods you can call to see the values specified in the annotation. + +```java +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; + +class Main { + void main() { + Class drinkClass = Drink.class; + + Field[] fields = drinkClass.getFields(); + for (var field : fields) { + IO.print(field.getName()); + IO.print(" - "); + + Special annotationValue = field.getAnnotation(Special.class); + if (annotationValue == null) { + IO.println("is not special."); + } + else if (annotationValue.isSuperDuperSpecial()) { + IO.println("is super-duper special."); + } + else { + IO.println("is special."); + } + } + } +} + +@Retention(RetentionPolicy.RUNTIME) +@interface Special { + boolean isSuperDuperSpecial() default false; +} + +class Drink { + public String name; + + @Special + public boolean caffeinated; + + @Special(isSuperDuperSpecial = true) + public String cost; + + public int height; +} +``` + +You can make use of this to implement a **very** wide variety of programs. \ No newline at end of file diff --git a/src/annotations/retention.md b/src/annotations/retention.md new file mode 100644 index 0000000..b60851c --- /dev/null +++ b/src/annotations/retention.md @@ -0,0 +1,25 @@ +# @Retention + +The next important meta-annotation is `@Retention`. + +If you want to be able to access an annotation using reflection +it needs to be marked with a retention policy of `RUNTIME`. + +```java,no_run +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@interface Special { +} +``` + +The other two options are `SOURCE` - which will throw away any evidence of the annotation after Java +compiles your code - and `CLASS` - which will save the annotation, but not in a way you can use at runtime. + +If you are making your own programs that make use of annotations, chances are you will be working with them at runtime. This means you will most likely be using `RUNTIME` more than the other options. + +That being said, there is nothing particularly wrong with `SOURCE` and `CLASS`. Its just that writing programs that use the annotations at those steps require some more work and there aren't too many downsides to `RUNTIME`. \ No newline at end of file diff --git a/src/annotations/target.md b/src/annotations/target.md new file mode 100644 index 0000000..81e5f9d --- /dev/null +++ b/src/annotations/target.md @@ -0,0 +1,24 @@ +# @Target + +Annotations can mark most parts of your program. Annotations are a part of your program. +Therefore annotations can have annotations. + +The first of these "meta-annotations" you are likely to use is `@Target`. +By default an annotation can mark anything, but you can use `@Target` to +restrict what can be marked.[^typeuse] + +```java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +// @Even can now only be used on fields and methods, +// but not classes +@Target({ + ElementType.FIELD, + ElementType.METHOD +}) +@interface Even { +} +``` + +[^typeuse]: The exception to this is the `TYPE_USE` target. That one needs to be explicitly specified. \ No newline at end of file diff --git a/src/annotations/usage.md b/src/annotations/usage.md new file mode 100644 index 0000000..fa1d9af --- /dev/null +++ b/src/annotations/usage.md @@ -0,0 +1,27 @@ +# Usage + +Once you have defined an annotation you can use it to mark different elements +of your program. + +```java,no_run +@interface Even { +} + +@interface NumberWrapper { +} + +@NumberWrapper // Here @NumberWrapper is annotating the EvenNumber class +class EvenNumber { + final @Even int x; // And @Even is annotating the "x" field + + EvenNumber(int x) { + if (x % 2 != 0) { + throw new IllegalArgumentException(Integer.toString(x)); + } + this.x = x; + } +} +``` + +You can place an annotation on nearly any "structural" element of a program. +This includes classes, method definitions, fields, and more. diff --git a/src/annotations/usage_with_elements.md b/src/annotations/usage_with_elements.md new file mode 100644 index 0000000..4ae0518 --- /dev/null +++ b/src/annotations/usage_with_elements.md @@ -0,0 +1,67 @@ +# Usage with Elements + +If an annotation is declared with elements then you must specify +values for those elements when annotating a part of your program. + +```java,no_run +@interface Todo { + String description(); +} + +@Todo(description="Actually write code") +class Code { +} +``` + +For `String`, `int`, and `boolean` elements you do this using their corresponding literals. +For `Class` elements you use `ClassName.class`. And for arrays you use array initializers. + +```java,no_run +enum Priority { + HIGH, + LOW +} + +@interface Todo { + String description(); + + int someNumber(); + + boolean isImportant(); + + Class someRelatedClass(); + + String[] notes(); + + Priority priority(); +} + +@Todo( + description = "Write some code", + someNumber = 444, + isImportant = false, + someRelatedClass = ArrayList.class, + notes = { + "this example is potentially confusing", + "it isn't high priority enough to fix" + }, + priority = Priority.LOW +) +class Code { + +} +``` + +If the only element you need to provide is named `value`, then you don't need to give its name. + + +```java,no_run +@interface Todo { + // The name "value" is special + String value(); +} + +@Todo("Write some code") +class Code { +} +``` diff --git a/src/arguments.md b/src/arguments.md index 026bd52..dc9119a 100644 --- a/src/arguments.md +++ b/src/arguments.md @@ -6,7 +6,7 @@ The way to customize what happens when a method is called is to have them take " ```java void sayHello(String name) { - System.out.println("Hello " + name + "!"); + IO.println("Hello " + name + "!"); } void main() { diff --git a/src/arguments/aliasing.md b/src/arguments/aliasing.md index a27b6dc..3c8b7d5 100644 --- a/src/arguments/aliasing.md +++ b/src/arguments/aliasing.md @@ -12,14 +12,14 @@ void main() { int[] nums = new int[] { 8 }; // The first number is 8 - System.out.println( + IO.println( "The first number is " + nums[0] ); incrementFirst(nums); // Now it is 9 - System.out.println( + IO.println( "Now it is " + nums[0] ); } diff --git a/src/arguments/challenges.md b/src/arguments/challenges.md index e7c1229..810facd 100644 --- a/src/arguments/challenges.md +++ b/src/arguments/challenges.md @@ -16,16 +16,16 @@ The `size` argument should control how big of a square is output. void main() { printSquare(4); - System.out.println(); + IO.println(); printSquare(3); - System.out.println(); + IO.println(); printSquare(2); - System.out.println(); + IO.println(); printSquare(1); - System.out.println(); + IO.println(); } ``` @@ -41,13 +41,13 @@ was given. void main() { printSquare(3); - System.out.println(); + IO.println(); printSquare(-3); - System.out.println(); + IO.println(); - System.out.println(); + IO.println(); printSquare(-2); - System.out.println(); + IO.println(); printSquare(2); } ``` @@ -74,15 +74,15 @@ Call the defined methods in a way that outputs "I did it!" ```java,editable void i() { - System.out.print("I"); + IO.print("I"); } void did(String what) { - System.out.println("did " + what); + IO.println("did " + what); } void space() { - System.out.print(" "); + IO.print(" "); } void main() { diff --git a/src/arguments/declaration.md b/src/arguments/declaration.md index fad2eb4..d162200 100644 --- a/src/arguments/declaration.md +++ b/src/arguments/declaration.md @@ -9,14 +9,14 @@ Each argument declaration looks the same as a variable declaration and has both // This declares a single argument named "food" that // has a type of "String". void eat(String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); } // This declares two arguments // "to", which is a String and // "age", which is an int. void happyBirthday(String to, int age) { - System.out.println( + IO.println( "Happy " + age + "th birthday " + to + "!" ); } diff --git a/src/arguments/final_arguments.md b/src/arguments/final_arguments.md index 70903f6..60aedd9 100644 --- a/src/arguments/final_arguments.md +++ b/src/arguments/final_arguments.md @@ -5,7 +5,7 @@ Just like normal variable declarations, arguments can be marked ```java void eat(final String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); } void main() { @@ -17,10 +17,10 @@ If you try to reassign a final argument, Java will not accept your program. ```java,panics void eat(final String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); // Will not work food = "toast"; - System.out.println(food); + IO.println(food); } void main() { diff --git a/src/arguments/inferred_types.md b/src/arguments/inferred_types.md index 5426118..cfd507a 100644 --- a/src/arguments/inferred_types.md +++ b/src/arguments/inferred_types.md @@ -20,7 +20,7 @@ You must always explicitly write out the types of arguments. ```java,no_run void makeHorchata(double milkFatPercent) { - System.out.println( + IO.println( "Making a horchata with " + milkFatPercent + "% milk." ); } diff --git a/src/arguments/invocation_with_arguments.md b/src/arguments/invocation_with_arguments.md index 1c8800f..79ce887 100644 --- a/src/arguments/invocation_with_arguments.md +++ b/src/arguments/invocation_with_arguments.md @@ -6,11 +6,11 @@ of literals or variable names ending with `)`. ```java void eat(String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); } void happyBirthday(String to, int age) { - System.out.println( + IO.println( "Happy " + age + "th birthday " + to + "!" ); } diff --git a/src/arguments/overloading.md b/src/arguments/overloading.md index c96dcf5..fbc57eb 100644 --- a/src/arguments/overloading.md +++ b/src/arguments/overloading.md @@ -6,15 +6,15 @@ or different numbers of arguments. ```java,no_run void doThing(int x) { - System.out.println(x); + IO.println(x); } void doThing(String name) { - System.out.println("Hello " + name); + IO.println("Hello " + name); } void doThing(int x, int y) { - System.out.println(x + y); + IO.println(x + y); } ``` @@ -24,15 +24,15 @@ you are passing. ```java void doThing(int x) { - System.out.println(x); + IO.println(x); } void doThing(String name) { - System.out.println("Hello " + name); + IO.println("Hello " + name); } void doThing(int x, int y) { - System.out.println(x + y); + IO.println(x + y); } void main() { diff --git a/src/arguments/reassignment.md b/src/arguments/reassignment.md index 7d71fd7..0c898de 100644 --- a/src/arguments/reassignment.md +++ b/src/arguments/reassignment.md @@ -6,9 +6,9 @@ within the method body; ```java void eat(String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); food = "nothing"; - System.out.println("Now I have " + food); + IO.println("Now I have " + food); } void main() { @@ -21,15 +21,15 @@ any variables passed to the method by the caller. ```java void eat(String food) { - System.out.println("I ate " + food); + IO.println("I ate " + food); food = "nothing"; - System.out.println("Now I have " + food); + IO.println("Now I have " + food); } void main() { String fruit = "apple"; eat(fruit); - System.out.println( + IO.println( "But in the caller I still have an " + fruit ); } diff --git a/src/array_list.md b/src/array_list.md index 0d6784e..e0a10a4 100644 --- a/src/array_list.md +++ b/src/array_list.md @@ -10,6 +10,6 @@ void main() { ArrayList names = new ArrayList<>(); names.add("John Wick"); - System.out.println(names); + IO.println(names); } ``` \ No newline at end of file diff --git a/src/array_list/add_an_item.md b/src/array_list/add_an_item.md index 377d1e4..4f1d0a7 100644 --- a/src/array_list/add_an_item.md +++ b/src/array_list/add_an_item.md @@ -11,7 +11,7 @@ void main() { names.add("The Bowery King"); names.add("Caine"); - System.out.println(names); + IO.println(names); } ``` diff --git a/src/array_list/get_an_item.md b/src/array_list/get_an_item.md index 110b467..05830af 100644 --- a/src/array_list/get_an_item.md +++ b/src/array_list/get_an_item.md @@ -11,7 +11,7 @@ void main() { names.add("Winston Scott"); String name = names.get(0); - System.out.println(name); + IO.println(name); } ``` @@ -26,6 +26,6 @@ void main() { names.add("Killa Harkan"); String name = names.get(10); - System.out.println(name); + IO.println(name); } ``` \ No newline at end of file diff --git a/src/array_list/loop_over_items.md b/src/array_list/loop_over_items.md index 72bff23..cd2444b 100644 --- a/src/array_list/loop_over_items.md +++ b/src/array_list/loop_over_items.md @@ -16,7 +16,7 @@ void main() { for (int i = 0; i < names.size(); i++) { String name = names.get(i); - System.out.println("NAME: " + name); + IO.println("NAME: " + name); } } ``` diff --git a/src/array_list/remove_an_item.md b/src/array_list/remove_an_item.md index d4be0db..5ad3826 100644 --- a/src/array_list/remove_an_item.md +++ b/src/array_list/remove_an_item.md @@ -16,11 +16,11 @@ void main() { names.add("The Elder"); names.add("The Harbinger"); - System.out.println(names); + IO.println(names); names.remove("The Elder"); - System.out.println(names); + IO.println(names); } ``` @@ -35,12 +35,12 @@ void main() { names.add("The Elder"); names.add("The Harbinger"); - System.out.println(names); + IO.println(names); names.remove(2); names.remove(0); - System.out.println(names); + IO.println(names); } ``` @@ -57,12 +57,12 @@ void main() { numbers.add(2); numbers.add(3); - System.out.println(numbers); + IO.println(numbers); // Notice that this removes "2" which is at index 1! numbers.remove(1); - System.out.println(numbers); + IO.println(numbers); } ``` @@ -82,10 +82,10 @@ void main() { numbers.add(2); numbers.add(3); - System.out.println(numbers); + IO.println(numbers); numbers.remove(Integer.valueOf(1)); - System.out.println(numbers); + IO.println(numbers); } ``` \ No newline at end of file diff --git a/src/array_list/set_an_item.md b/src/array_list/set_an_item.md index 766ed00..edca5f8 100644 --- a/src/array_list/set_an_item.md +++ b/src/array_list/set_an_item.md @@ -11,8 +11,8 @@ void main() { names.set(0, "Baba Yaga"); - System.out.println(names); - System.out.println(names.get(0)); + IO.println(names); + IO.println(names.get(0)); } ``` diff --git a/src/array_list/size.md b/src/array_list/size.md index 7f0ed72..88a58c5 100644 --- a/src/array_list/size.md +++ b/src/array_list/size.md @@ -9,13 +9,13 @@ import java.util.ArrayList; void main() { ArrayList names = new ArrayList<>(); - System.out.println(names.size()); + IO.println(names.size()); names.add("Vincent Bisset de Gramont"); - System.out.println(names.size()); + IO.println(names.size()); names.add("Mr. Nobody"); - System.out.println(names.size()); + IO.println(names.size()); } ``` diff --git a/src/arrays/access_individual_elements.md b/src/arrays/access_individual_elements.md index 17eb48e..61ce817 100644 --- a/src/arrays/access_individual_elements.md +++ b/src/arrays/access_individual_elements.md @@ -11,13 +11,13 @@ The first element can be accessed by using `0`, the second by using `1`, and so String[] lyrics = { "you", "say", "goodbye" }; String you = lyrics[0]; -System.out.println(you); +IO.println(you); String say = lyrics[1]; -System.out.println(say); +IO.println(say); String goodbye = lyrics[2]; -System.out.println(goodbye); +IO.println(goodbye); ~} ``` @@ -28,7 +28,7 @@ The index of the element can also come from a variable. int index = 2; String[] lyrics = { "I", "say", "hello" }; -System.out.println(lyrics[index]); +IO.println(lyrics[index]); ~} ``` @@ -39,7 +39,7 @@ you will get an error. ~void main(){ String[] lyrics = { "I", "say", "hi" }; // Crash! -System.out.println(lyrics[999]); +IO.println(lyrics[999]); ~} ``` @@ -47,6 +47,6 @@ System.out.println(lyrics[999]); ~void main(){ String[] lyrics = { "you", "say", "low" }; // Crash! -System.out.println(lyrics[-1]); +IO.println(lyrics[-1]); ~} ``` diff --git a/src/arrays/aliasing.md b/src/arrays/aliasing.md index 8ac0622..07a1455 100644 --- a/src/arrays/aliasing.md +++ b/src/arrays/aliasing.md @@ -12,23 +12,23 @@ char[] lettersOne = { 'B', 'a', 't', 'm', 'a', 'n' }; char[] lettersTwo = lettersOne; // Batman -System.out.println(lettersOne); +IO.println(lettersOne); // Batman -System.out.println(lettersTwo); +IO.println(lettersTwo); lettersOne[0] = 'C'; // Catman -System.out.println(lettersOne); +IO.println(lettersOne); // Catman -System.out.println(lettersTwo); +IO.println(lettersTwo); lettersTwo[0] = 'R'; // Ratman -System.out.println(lettersOne); +IO.println(lettersOne); // Ratman -System.out.println(lettersTwo); +IO.println(lettersTwo); ~} ``` diff --git a/src/arrays/challenges.md b/src/arrays/challenges.md index 48c2d91..e8b1eb9 100644 --- a/src/arrays/challenges.md +++ b/src/arrays/challenges.md @@ -19,7 +19,7 @@ void main() { ## Challenge 2 -Using only `System.out.println` and array accesses, +Using only `IO.println` and array accesses, print `hello world` to the screen. ```java,editable @@ -59,7 +59,7 @@ void main() { total += numbers[index]; index += 1; } - System.out.println(total); + IO.println(total); } ``` @@ -78,6 +78,6 @@ void main() { // ----------- char[] toPrint = name; - System.out.println(toPrint); + IO.println(toPrint); } ``` diff --git a/src/arrays/difference_between_initializer_and_literal.md b/src/arrays/difference_between_initializer_and_literal.md index 9e01433..eb12780 100644 --- a/src/arrays/difference_between_initializer_and_literal.md +++ b/src/arrays/difference_between_initializer_and_literal.md @@ -10,7 +10,7 @@ then use that `String` afterwards. ~void main() { String name = "Alana"; // l -System.out.println(name.charAt(1)); +IO.println(name.charAt(1)); ~} ``` @@ -19,7 +19,7 @@ But you can also perform those operations using the literal itself, without an i ```java ~void main() { // l -System.out.println("Alana".charAt(1)); +IO.println("Alana".charAt(1)); ~} ``` @@ -30,7 +30,7 @@ the array. ~void main() { char[] name = { 'A', 'm', 'a', 'n', 'd', 'a' }; // m -System.out.println(name[1]); +IO.println(name[1]); ~} ``` @@ -39,6 +39,6 @@ But they do not work to perform operations on directly. ```java ~void main() { // Will not run -System.out.println({ 'A', 'm', 'a', 'n', 'd', 'a' }[1]); +IO.println({ 'A', 'm', 'a', 'n', 'd', 'a' }[1]); ~} ``` diff --git a/src/arrays/empty_array.md b/src/arrays/empty_array.md index 6f3465f..531962f 100644 --- a/src/arrays/empty_array.md +++ b/src/arrays/empty_array.md @@ -16,9 +16,9 @@ be able to reassign the variable holding it when you get some. char[] emptyCharArray = {}; // 0 -System.out.println(emptyCharArray.length); +IO.println(emptyCharArray.length); // Crash -System.out.println(emptyCharArray[0]); +IO.println(emptyCharArray[0]); ~} ``` \ No newline at end of file diff --git a/src/arrays/initialization_with_new.md b/src/arrays/initialization_with_new.md index b916804..1275a4a 100644 --- a/src/arrays/initialization_with_new.md +++ b/src/arrays/initialization_with_new.md @@ -7,10 +7,10 @@ and an empty `[]`. ```java ~void main() { char[] mainCharacter = { 'A', 'a', 'n', 'g' }; -System.out.println(mainCharacter); +IO.println(mainCharacter); char[] sideCharacter = new char[] { 'A', 'a', 'n', 'g' }; -System.out.println(sideCharacter); +IO.println(sideCharacter); ~} ``` @@ -22,7 +22,7 @@ holding an array. char[] element; element = new char[] { 'f', 'i', 'r', 'e' }; -System.out.println(element); +IO.println(element); // This would not work // element = { 'f', 'i', 'r', 'e' }; @@ -34,6 +34,6 @@ the initializer coupled with the `new char[]` is akin to an "array expression." ```java ~void main() { -System.out.println(new char[]{ 'K', 'a', 't', 'a', 'r', 'a' }[1]); +IO.println(new char[]{ 'K', 'a', 't', 'a', 'r', 'a' }[1]); ~} ``` \ No newline at end of file diff --git a/src/arrays/length.md b/src/arrays/length.md index 82c269f..1cb840e 100644 --- a/src/arrays/length.md +++ b/src/arrays/length.md @@ -8,7 +8,7 @@ String[] veggies = { "brussels", "cabbage", "carrots" }; int numberOfElements = veggies.length; // veggies is 3 elements long -System.out.println( +IO.println( "veggies is " + numberOfElements + " characters long" ); ~} diff --git a/src/arrays/printing_the_contents_of_an_array.md b/src/arrays/printing_the_contents_of_an_array.md index cae4796..71c4c24 100644 --- a/src/arrays/printing_the_contents_of_an_array.md +++ b/src/arrays/printing_the_contents_of_an_array.md @@ -1,6 +1,6 @@ # Printing the Contents of an Array -If you try to use `System.out.println` to output a `String[]` +If you try to use `IO.println` to output a `String[]` you won't see the contents of the array. Instead you will see something like `[Ljava.lang.String;@1c655221`. @@ -8,7 +8,7 @@ something like `[Ljava.lang.String;@1c655221`. ~void main() { String[] shout = { "fus", "ro", "dah" }; // [Ljava.lang.String;@5a07e868 -System.out.println(shout); +IO.println(shout); ~} ``` @@ -18,15 +18,15 @@ A similar thing will happen with `int[]`, `boolean[]`, and `double[]`.[^gibberis ~void main() { int[] nums = { 11, 11, 11 }; // [I@5a07e868 -System.out.println(nums); +IO.println(nums); boolean[] bools = { true, false }; // [Z@5a07e868 -System.out.println(bools); +IO.println(bools); double[] doubles = { 1.1, 1.1, 1.1 }; // [D@5a07e868 -System.out.println(bools); +IO.println(bools); ~} ``` @@ -37,7 +37,7 @@ It will be printed as if it were a `String`. ~void main() { char[] continent = { 'T', 'a', 'm', 'r', 'i', 'e', 'l' }; // Tamriel -System.out.println(continent); +IO.println(continent); ~} ``` @@ -50,7 +50,7 @@ String[] factions = { "empire", "stormcloaks", "dragons" }; int index = 0; while (index < factions.length) { - System.out.println(factions[index]); + IO.println(factions[index]); index++; } ~} diff --git a/src/arrays/reassignment.md b/src/arrays/reassignment.md index 834d93b..f60bcdc 100644 --- a/src/arrays/reassignment.md +++ b/src/arrays/reassignment.md @@ -12,11 +12,11 @@ So to reassign an `int[]` you need to write something like `new int[] { 1, 2, 3 ~void main() { int[] numbers = { 1, 2 }; // 2 -System.out.println(numbers.length); +IO.println(numbers.length); numbers = new int[] { numbers[0], numbers[1], 3 }; // 3 -System.out.println(numbers.length); +IO.println(numbers.length); ~} ``` @@ -28,29 +28,29 @@ are aliases for the variable's old value. char[] wordOne = { 'g', 'o' }; char[] wordTwo = wordOne; // go -System.out.println(wordOne); +IO.println(wordOne); // go -System.out.println(wordTwo); +IO.println(wordTwo); wordOne = new char[] { wordOne[0], wordOne[1], 's', 'h' }; // gosh -System.out.println(wordOne); +IO.println(wordOne); // go -System.out.println(wordTwo); +IO.println(wordTwo); wordTwo[0] = 'n'; // gosh -System.out.println(wordOne); +IO.println(wordOne); // no -System.out.println(wordTwo); +IO.println(wordTwo); wordOne[0] = 'p'; // posh -System.out.println(wordOne); +IO.println(wordOne); // no -System.out.println(wordTwo); +IO.println(wordTwo); ~} ``` diff --git a/src/arrays/relation_to_final_variables.md b/src/arrays/relation_to_final_variables.md index 4d606a1..8c65fee 100644 --- a/src/arrays/relation_to_final_variables.md +++ b/src/arrays/relation_to_final_variables.md @@ -9,7 +9,7 @@ that the array's contents cannot be changed directly or through an alias. ~void main() { final char[] catchphrase = { 'w', 'o', 'a', 'h', '!' }; // woah! -System.out.println(catchphrase); +IO.println(catchphrase); // Cannot reassign // catchphrase = new char[] { 'e', 'g', 'a', 'd', 's' } @@ -24,6 +24,6 @@ alias[3] = 'd'; alias[4] = 's'; // egads -System.out.println(catchphrase); +IO.println(catchphrase); ~} ``` diff --git a/src/arrays/set_individual_elements.md b/src/arrays/set_individual_elements.md index ecd9243..5209c80 100644 --- a/src/arrays/set_individual_elements.md +++ b/src/arrays/set_individual_elements.md @@ -9,7 +9,7 @@ the new value.[^strings] ```java ~void main() { String[] sentence = { "you", "are", "found", "guilty" }; -System.out.println( +IO.println( sentence[0] + " " + sentence[1] @@ -20,7 +20,7 @@ System.out.println( ); sentence[1] = "aren't"; -System.out.println( +IO.println( sentence[0] + " " + sentence[1] @@ -38,7 +38,7 @@ The index of the element to set can also come from a variable. ~void main() { int index = 2; String[] response = { "and", "it", "isn't", "opposite", "day" }; -System.out.println( +IO.println( response[0] + " " + response[1] @@ -51,7 +51,7 @@ System.out.println( ); response[2] = "is"; -System.out.println( +IO.println( response[0] + " " + response[1] diff --git a/src/arrays_ii/challenges.md b/src/arrays_ii/challenges.md index fe24ea1..ca4817c 100644 --- a/src/arrays_ii/challenges.md +++ b/src/arrays_ii/challenges.md @@ -16,7 +16,7 @@ void main() { String[] empty = ???; // Should be 0 - System.out.println(empty.length); + IO.println(empty.length); } ``` @@ -44,7 +44,7 @@ void main() { for (int i = 0; i < prices.length; i++) { double price = prices[i]; - System.out.println(price); + IO.println(price); } } ``` @@ -62,7 +62,7 @@ void main() { // CODE HERE // ---------- - System.out.println(sandwich); + IO.println(sandwich); } ``` @@ -85,7 +85,7 @@ void main() { // CODE HERE // ---------- - System.out.println(triangle); + IO.println(triangle); } ``` @@ -102,10 +102,10 @@ char[] buildTriangle(int height) { } void main() { - System.out.println(buildTriangle(3)); - System.out.println("--------------"); - System.out.println(buildTriangle(5)); - System.out.println("--------------"); - System.out.println(buildTriangle(2)); + IO.println(buildTriangle(3)); + IO.println("--------------"); + IO.println(buildTriangle(5)); + IO.println("--------------"); + IO.println(buildTriangle(2)); } ``` \ No newline at end of file diff --git a/src/arrays_ii/default_values.md b/src/arrays_ii/default_values.md index 7345f27..edf86c6 100644 --- a/src/arrays_ii/default_values.md +++ b/src/arrays_ii/default_values.md @@ -9,11 +9,11 @@ For primitive types like `int` and `double`, each element will be initialized to ~void main() { int[] digits = new int[10]; // 0 -System.out.println(digits[0]); +IO.println(digits[0]); double[] readings = new double[5]; // 0.0 -System.out.println(readings[0]); +IO.println(readings[0]); ~} ``` @@ -23,7 +23,7 @@ For `boolean`, each element will be initialized to `false`.[^funfact] ~void main() { boolean[] pokedex = new boolean[10]; // false -System.out.println(pokedex[0]); +IO.println(pokedex[0]); ~} ``` @@ -34,11 +34,11 @@ the default value will be `null`. ~void main() { String[] names = new String[10]; // null -System.out.println(names[0]); +IO.println(names[0]); Integer[] scores = new Integer[26]; // null -System.out.println(scores[0]); +IO.println(scores[0]); ~} ``` diff --git a/src/arrays_ii/populate_arrays.md b/src/arrays_ii/populate_arrays.md index 393056b..effe3d4 100644 --- a/src/arrays_ii/populate_arrays.md +++ b/src/arrays_ii/populate_arrays.md @@ -11,6 +11,6 @@ char[] letters = new char[26]; for (int i = 0; i < letters.length; i++) { letters[i] = (char) ('a' + i); } -System.out.println(letters); +IO.println(letters); ~} ``` diff --git a/src/boolean/challenges.md b/src/boolean/challenges.md index 5e204f3..e209490 100644 --- a/src/boolean/challenges.md +++ b/src/boolean/challenges.md @@ -18,7 +18,7 @@ void main() { boolean result = a || b && c || !d; - System.out.println(result); + IO.println(result); } ``` @@ -35,7 +35,7 @@ void main() { boolean result = !(a || b && c || !d) || (a && b || c); - System.out.println(result); + IO.println(result); } ``` @@ -51,7 +51,7 @@ void main() { boolean validChoice = < YOUR CODE HERE >; - System.out.println(validChoice); + IO.println(validChoice); } ``` diff --git a/src/boolean/not.md b/src/boolean/not.md index 0b20f8c..925d5f8 100644 --- a/src/boolean/not.md +++ b/src/boolean/not.md @@ -4,7 +4,7 @@ Booleans can also be "negated" using the "not" operator - `!`. ```java,no_run boolean haveOreosInHouse = true; -boolean stuckToCalorieLimit = !haveOreos; +boolean stuckToCalorieLimit = !haveOreosInHouse; ``` So in this case, I have stuck to my calorie limit if there are _not_ Oreos in the house. diff --git a/src/boxed_primitives.md b/src/boxed_primitives.md index 90e5947..c979832 100644 --- a/src/boxed_primitives.md +++ b/src/boxed_primitives.md @@ -8,10 +8,10 @@ For this reason there are versions of those primitive[^primitive] types which do ```java void sayAge(Integer age) { if (age == null) { - System.out.println("Age is not yet known"); + IO.println("Age is not yet known"); } else { - System.out.println("Age is " + age); + IO.println("Age is " + age); } } diff --git a/src/boxed_primitives/boolean.md b/src/boxed_primitives/boolean.md index 49e68ea..23a878a 100644 --- a/src/boxed_primitives/boolean.md +++ b/src/boxed_primitives/boolean.md @@ -6,7 +6,7 @@ The type to use for a `boolean` that might be null is `Boolean`. ```java ~void main() { Boolean b = null; -System.out.println(b); +IO.println(b); b = true; -System.out.println(b); +IO.println(b); ~} \ No newline at end of file diff --git a/src/boxed_primitives/boxing_conversion.md b/src/boxed_primitives/boxing_conversion.md index 13e2449..6e1f7a3 100644 --- a/src/boxed_primitives/boxing_conversion.md +++ b/src/boxed_primitives/boxing_conversion.md @@ -9,8 +9,8 @@ automatically do that conversion.[^obvious] int x = 5; Integer y = x; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` diff --git a/src/boxed_primitives/challenges.md b/src/boxed_primitives/challenges.md index d0506c4..53908ac 100644 --- a/src/boxed_primitives/challenges.md +++ b/src/boxed_primitives/challenges.md @@ -22,7 +22,7 @@ int compute(int x) { } void main() { - System.out.println(compute(5)); + IO.println(compute(5)); } ``` @@ -70,22 +70,22 @@ If the number is less than or equal to zero, return `null`. void main() { // 45 - System.out.println( + IO.println( onlyPositive(45) ); // 46 - System.out.println( + IO.println( onlyPositive(45) + 1 ); // null - System.out.println( + IO.println( onlyPositive(0) ); // null - System.out.println( + IO.println( onlyPositive(-1) ); } @@ -102,7 +102,7 @@ void main() { int birds = ducks + sparrows; - System.out.println(birds); + IO.println(birds); } ``` @@ -115,7 +115,7 @@ void main() { char[] face = new char[] { ':', ')' }; Character[] smile = face; - System.out.println(smile); + IO.println(smile); } ``` @@ -132,6 +132,6 @@ void main() { smile[i] = face[i]; } - System.out.println(smile); + IO.println(smile); } ``` \ No newline at end of file diff --git a/src/boxed_primitives/character.md b/src/boxed_primitives/character.md index acd43d3..d3365d8 100644 --- a/src/boxed_primitives/character.md +++ b/src/boxed_primitives/character.md @@ -5,9 +5,9 @@ The type to use for a `char` that might be null is `Character`. ```java ~void main() { Character c = null; -System.out.println(c); +IO.println(c); c = '%'; -System.out.println(c); +IO.println(c); ~} ``` @@ -17,9 +17,9 @@ shown when printed. ```java ~void main() { char[] c1 = new char[] { 'a', 'b', 'c' }; -System.out.println(c1); +IO.println(c1); Character[] c2 = new Character[] { 'a', 'b', 'c' }; -System.out.println(c2); +IO.println(c2); ~} ``` \ No newline at end of file diff --git a/src/boxed_primitives/double.md b/src/boxed_primitives/double.md index 97402b7..7cc1e6d 100644 --- a/src/boxed_primitives/double.md +++ b/src/boxed_primitives/double.md @@ -5,9 +5,9 @@ The type to use for a `double` that might be null is `Double`. ```java ~void main() { Double d = null; -System.out.println(d); +IO.println(d); d = 3.14; -System.out.println(d); +IO.println(d); ~} ``` @@ -17,6 +17,6 @@ get a `NullPointerException`. ```java,panics ~void main() { Double d = null; -System.out.println(d + 1); +IO.println(d + 1); ~} ``` \ No newline at end of file diff --git a/src/boxed_primitives/integer.md b/src/boxed_primitives/integer.md index dc2c83b..aa1de81 100644 --- a/src/boxed_primitives/integer.md +++ b/src/boxed_primitives/integer.md @@ -5,9 +5,9 @@ The type to use for an `int` that might be null is `Integer`. ```java ~void main() { Integer i = null; -System.out.println(i); +IO.println(i); i = 5; -System.out.println(i); +IO.println(i); ~} ``` @@ -17,6 +17,6 @@ get a `NullPointerException`. ```java,panics ~void main() { Integer i = null; -System.out.println(i * 5); +IO.println(i * 5); ~} ``` \ No newline at end of file diff --git a/src/boxed_primitives/unboxing_conversion.md b/src/boxed_primitives/unboxing_conversion.md index c0005f9..6a06643 100644 --- a/src/boxed_primitives/unboxing_conversion.md +++ b/src/boxed_primitives/unboxing_conversion.md @@ -11,7 +11,7 @@ Integer x = 5; int y = 3; int z = x * y; -System.out.println(z); +IO.println(z); ~} ``` @@ -21,7 +21,7 @@ As well as `Boolean`s in logical expressions. ~void main() { Boolean hasHat = true; if (hasHat) { - System.out.println("You have a hat!"); + IO.println("You have a hat!"); } ~} ``` diff --git a/src/branching_logic/boolean_expressions.md b/src/branching_logic/boolean_expressions.md index be6542f..754584d 100644 --- a/src/branching_logic/boolean_expressions.md +++ b/src/branching_logic/boolean_expressions.md @@ -18,7 +18,7 @@ else { // or // boolean canRent = age > 25 ? true : false; -System.out.println(canRent); +IO.println(canRent); ~} ``` @@ -30,6 +30,6 @@ itself already evaluates to a `boolean`. You can directly assign the variable to int age = 22; boolean canRent = age > 25; -System.out.println(canRent); +IO.println(canRent); ~} ``` diff --git a/src/branching_logic/challenges.md b/src/branching_logic/challenges.md index 213d4b5..452b2fa 100644 --- a/src/branching_logic/challenges.md +++ b/src/branching_logic/challenges.md @@ -57,6 +57,6 @@ void main() { // < YOUR CODE HERE > - System.out.println(message); + IO.println(message); } ``` diff --git a/src/branching_logic/conditional_operator.md b/src/branching_logic/conditional_operator.md index de895a0..91c2b28 100644 --- a/src/branching_logic/conditional_operator.md +++ b/src/branching_logic/conditional_operator.md @@ -12,7 +12,7 @@ String message = age < 25 ? "You cannot rent a car!" : "You might be able to rent a car"; -System.out.println(message); +IO.println(message); ~} ``` diff --git a/src/branching_logic/else.md b/src/branching_logic/else.md index 940fa27..33cef23 100644 --- a/src/branching_logic/else.md +++ b/src/branching_logic/else.md @@ -7,10 +7,10 @@ and another when that same condition evaluates to `false` you can use `else`. ~void main() { int age = 30; // 🙎‍♀️ if (age < 25) { - System.out.println("You cannot rent a car!"); + IO.println("You cannot rent a car!"); } else { - System.out.println("You might be able to rent a car."); + IO.println("You might be able to rent a car."); } ~} ``` @@ -34,7 +34,7 @@ When the condition evaluates to `false`, the code inside of `else`'s `{` and `}` ```java,does_not_compile ~void main() { else { - System.out.println("No if."); + IO.println("No if."); } ~} ``` diff --git a/src/branching_logic/else_if.md b/src/branching_logic/else_if.md index 1c0a04d..db6ae23 100644 --- a/src/branching_logic/else_if.md +++ b/src/branching_logic/else_if.md @@ -8,14 +8,14 @@ If you have an `if` nested in an `else` branch, you can simplify that by using boolean cool = true; // 🕶️ int age = 30; // 🙎‍♀️ if (age < 25) { - System.out.println("You cannot rent a car!"); + IO.println("You cannot rent a car!"); } else { if (!cool) { - System.out.println("You failed the vibe check."); + IO.println("You failed the vibe check."); } else { - System.out.println("You are rad enough to rent a car."); + IO.println("You are rad enough to rent a car."); } } ~} @@ -29,13 +29,13 @@ boolean cool = true; // 🕶️ int age = 30; // 🙎‍♀️ if (age < 25) { - System.out.println("You cannot rent a car!"); + IO.println("You cannot rent a car!"); } else if (!cool) { - System.out.println("You failed the vibe check."); + IO.println("You failed the vibe check."); } else { - System.out.println("You are rad enough to rent a car."); + IO.println("You are rad enough to rent a car."); } ~} ``` @@ -49,19 +49,19 @@ boolean cool = true; // 🕶️ int age = 100; // 👴 if (age < 25) { - System.out.println("You cannot rent a car!"); + IO.println("You cannot rent a car!"); } else if (!cool) { - System.out.println("You failed the vibe check."); + IO.println("You failed the vibe check."); } else if (age > 99) { - System.out.println("You are too old to safely drive a car!"); + IO.println("You are too old to safely drive a car!"); } else if (age > 450) { - System.out.println("There can only be one! ⚔️🏴󠁧󠁢󠁳󠁣󠁴󠁿"); + IO.println("There can only be one! ⚔️🏴󠁧󠁢󠁳󠁣󠁴󠁿"); } else { - System.out.println("You are rad enough to rent a car."); + IO.println("You are rad enough to rent a car."); } ~} ``` diff --git a/src/branching_logic/if.md b/src/branching_logic/if.md index 93e1a5d..3ec3a74 100644 --- a/src/branching_logic/if.md +++ b/src/branching_logic/if.md @@ -6,7 +6,7 @@ The way to represent a branching path in Java is by using an `if` statement. ~void main() { int age = 5; // 👶 if (age < 25) { - System.out.println("You are too young to rent a car!"); + IO.println("You are too young to rent a car!"); } ~} ``` @@ -31,7 +31,7 @@ and you will be told that you cannot rent a car. ~void main() { int age = 80; // 👵 if (age < 25) { - System.out.println("You are too young to rent a car!"); + IO.println("You are too young to rent a car!"); } ~} ``` diff --git a/src/branching_logic/nested_ifs.md b/src/branching_logic/nested_ifs.md index a4e04d2..cd26f14 100644 --- a/src/branching_logic/nested_ifs.md +++ b/src/branching_logic/nested_ifs.md @@ -6,9 +6,9 @@ The code inside of the `{` and `}` can be anything, including more `if` statment ~void main() { int age = 5; // 👶 if (age < 25) { - System.out.println("You are too young to rent a car!"); + IO.println("You are too young to rent a car!"); if (age == 24) { - System.out.println("(but it was close)"); + IO.println("(but it was close)"); } } ~} diff --git a/src/branching_logic/relation_to_delayed_assignment.md b/src/branching_logic/relation_to_delayed_assignment.md index 570d54b..c9106d3 100644 --- a/src/branching_logic/relation_to_delayed_assignment.md +++ b/src/branching_logic/relation_to_delayed_assignment.md @@ -17,7 +17,7 @@ else { message = "You cannot rent a car!"; } -System.out.println(message); +IO.println(message); ~} ``` @@ -35,6 +35,6 @@ if (age > 25) { // message is not always given an initial value // so you cannot use it. -System.out.println(message); +IO.println(message); ~} ``` diff --git a/src/branching_logic/scoped_variables.md b/src/branching_logic/scoped_variables.md index a912580..096443f 100644 --- a/src/branching_logic/scoped_variables.md +++ b/src/branching_logic/scoped_variables.md @@ -9,12 +9,12 @@ int age = 5; if (age == 5) { int nextAge = age + 1; - System.out.println(nextAge); + IO.println(nextAge); } // If you uncomment this line, there will be an issue // `nextAge` is not available to the scope outside of the `if` -// System.out.println(nextAge); +// IO.println(nextAge); ~} ``` @@ -34,7 +34,7 @@ else { // This will not work, because although `message` is declared // in all branches, it is not declared in the "outer scope" -System.out.println(message); +IO.println(message); ~} ``` diff --git a/src/characters/challenges.md b/src/characters/challenges.md index 3f20799..94544fc 100644 --- a/src/characters/challenges.md +++ b/src/characters/challenges.md @@ -15,7 +15,7 @@ Try to work it out on paper before running the program below. void main() { char x = 'x'; - System.out.println(x * x); + IO.println(x * x); } ``` @@ -31,7 +31,7 @@ void main() { boolean isLetter = ???; - System.out.println(isLetter); + IO.println(isLetter); } ``` diff --git a/src/characters/conversion_from_integers.md b/src/characters/conversion_from_integers.md index 7ae9056..76211fb 100644 --- a/src/characters/conversion_from_integers.md +++ b/src/characters/conversion_from_integers.md @@ -9,7 +9,7 @@ int x = 120; char xAsChar = (char) x; -System.out.println(xAsChar); +IO.println(xAsChar); ~} ``` @@ -21,6 +21,6 @@ The initial value of a `char` can also be given by an integer literal if the int ~void main() { char z = 122; -System.out.println(z); +IO.println(z); ~} ``` diff --git a/src/characters/conversion_to_integers.md b/src/characters/conversion_to_integers.md index d3dccb6..4c152a4 100644 --- a/src/characters/conversion_to_integers.md +++ b/src/characters/conversion_to_integers.md @@ -10,7 +10,7 @@ by attempting to assign a `char` to an `int`. ~void main() { int valueOfA = 'a'; -System.out.println(valueOfA); +IO.println(valueOfA); ~} ``` @@ -23,11 +23,11 @@ char gee = 'g'; // all the letters from a to z have consecutive numeric values. boolean isLetter = gee >= 'a' && gee <= 'z'; -System.out.println(isLetter); +IO.println(isLetter); ~} ``` This can be useful if you are stranded on Mars[^onmars] or if you want to see if a character is in some range. -[^onmars]: https://www.youtube.com/watch?v=k-GH3mbvUro +[^onmars]: https://www.youtube.com/watch?v=0xkP_FQUsuM diff --git a/src/class_extension.md b/src/class_extension.md index 6a64a38..d2d3311 100644 --- a/src/class_extension.md +++ b/src/class_extension.md @@ -1 +1,9 @@ # Class Extension + +Just like interfaces can extend other interfaces, classes can extend other classes. + +```java +class BodyOfWater {} + +class Ocean extends BodyOfWater {} +``` \ No newline at end of file diff --git a/src/class_extension/abstract_classes.md b/src/class_extension/abstract_classes.md new file mode 100644 index 0000000..3a39f4f --- /dev/null +++ b/src/class_extension/abstract_classes.md @@ -0,0 +1,50 @@ +# Abstract Classes + +Abstract classes are classes which you cannot make an instance of directly.[^concrete] + +```java,does_not_compile +abstract class Plant {} + +void main() { + // You cannot make an instance because + // it is an abstract class. + var p = new Plant(); +} +``` + +The use-case for these is making classes which are designed to be subclassed. +An example of this that comes with Java is `AbstractList`. + +`AbstractList` defines most of the methods required by the `List` interface and is intended to +lower the effort required to make a custom `List` implementation. + +```java +import java.util.AbstractList; + +// This subclass is a List containing the numbers 5 and 7 +class FiveAndSeven + // Almost all of the implementation is inherited from AbstractList + extends AbstractList { + + @Override + public Integer get(int index) { + return switch (index) { + case 0 -> 5; + case 1 -> 7; + default -> throw new IndexOutOfBoundsException(); + }; + } + + @Override + public int size() { + return 2; + } +} + +void main() { + var l = new FiveAndSeven(); + IO.println(l); +} +``` + +[^concrete]: "Abstract" as a term here means something close to "not in reality." You will hear people refer to non-abstract classes as "concrete" classes. \ No newline at end of file diff --git a/src/class_extension/abstract_methods.md b/src/class_extension/abstract_methods.md new file mode 100644 index 0000000..991c02a --- /dev/null +++ b/src/class_extension/abstract_methods.md @@ -0,0 +1,74 @@ +# Abstract Methods + +Methods on an `abstract` class may themselves be marked `abstract`. + +```java +abstract class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + // All classes which extend BodyOfWater must + // define what happens when you swim. + abstract void swim(); +} +``` + +Any non-abstract class extending from an `abstract` class must give a definition +for `abstract` methods. + +```java +abstract class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + abstract void swim(); +} + +class Lake extends BodyOfWater { + // If you didn't define this it wouldn't work + @Override + void swim() { + IO.println("Relaxing time"); + } +} +``` + +But if an abstract class is being extended by another abstract class, you don't need to. + +```java,no_run +abstract class Liquid { + abstract double viscosity(); +} + +// Water doesn't need to define volume() +// because Water is abstract +abstract class Water extends Liquid { + abstract double purity(); +} + +// But as soon as you get to a "concrete" implementation +// you need to provide definitions +class TownWater extends Water { + @Override + double viscosity() { + return 0.01; + } + + @Override + double purity() { + return 0.99; + } +} +``` diff --git a/src/class_extension/extend_a_class.md b/src/class_extension/extend_a_class.md new file mode 100644 index 0000000..ea5c39b --- /dev/null +++ b/src/class_extension/extend_a_class.md @@ -0,0 +1,47 @@ +# Extend a Class + +For a class to extend another one you need to write `extends` and then the name of the class +which is being extended. + +```java +class BodyOfWater {} + +class Ocean extends BodyOfWater {} +``` + +If the class being extended has non-zero argument constructors, +the subclass must pass arguments to those constructors in it's constructor +using `super()`. + +```java +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + // Before you exit, you must pass + // arguments to the super-class constructor. + super(depth); + } +} + +void main() { + Ocean pacific = new Ocean("Pacific", 36201L); + IO.println( + "The " + pacific.name + + " ocean is " + pacific.depth + + "' deep." + ); +} +``` \ No newline at end of file diff --git a/src/class_extension/final_classes.md b/src/class_extension/final_classes.md new file mode 100644 index 0000000..6b378c1 --- /dev/null +++ b/src/class_extension/final_classes.md @@ -0,0 +1,42 @@ +# Final Classes + +If a class is marked `final` it cannot be extended. + +```java,does_not_compile +final class Apple {} + +// Cannot extend a final class +class RedApple extends Apple {} +~void main() {} +``` + +Because thinking about what the implications are if someone extends a class is hard, +it is reasonable to always declare your classes as `final` unless you have some reason not to. + +```java +// It's not that something bad would happen if someone +// extended this class. It's that not having to think +// about it is useful. +final class Saltine { + int saltiness; + + Saltine(int saltiness) { + this.saltiness = saltiness; + } +} +``` + +Records and Enums are always implicitly final. + +```java,does_not_compile +record Pos(int x, int y) {} + +// Records are final +class Pos2 extends Pos {} + +enum StopLight { RED, YELLOW, GREEN } + +// Enums are final +class StopLight2 extends StopLight {} +~class Main {void main() {}} +``` \ No newline at end of file diff --git a/src/class_extension/inheritance.md b/src/class_extension/inheritance.md new file mode 100644 index 0000000..ce87ee5 --- /dev/null +++ b/src/class_extension/inheritance.md @@ -0,0 +1,43 @@ +# Inheritance + +When one class extends another we say that it is "inheriting" +from the superclass. + +What this means is that all the fields and methods of the class being extended +carry over into the extending class. + +```java +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + void swim() { + if (depth > 50) { + IO.println("You have drowned"); + } + else { + IO.println("You are fine"); + } + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + super(depth); + } +} + +void main() { + BodyOfWater pacific = new Ocean("Pacific", 36201L); + pacific.swim(); // The swim method is "inherited" +} +``` \ No newline at end of file diff --git a/src/class_extension/override.md b/src/class_extension/override.md new file mode 100644 index 0000000..18d5768 --- /dev/null +++ b/src/class_extension/override.md @@ -0,0 +1,188 @@ +# Override + +Just as you can provide an alternative implementation for a default method +in an interface, you can override a method inherited from a superclass. + +```java +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + void swim() { + if (depth > 50) { + IO.println("You have drowned"); + } + else { + IO.println("You are fine"); + } + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + super(depth); + } + + @Override + void swim() { + IO.println("You have been eaten by a shark"); + } +} + +void main() { + BodyOfWater pacific = new Ocean("Pacific", 36201L); + pacific.swim(); +} +``` + +This can be prevented by marking a method as `final`. This explicitly disallows +overriding it in a subclass. + +```java,does_not_compile +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + // Cannot be overriden + final void swim() { + if (depth > 50) { + IO.println("You have drowned"); + } + else { + IO.println("You are fine"); + } + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + super(depth); + } + + @Override + void swim() { // We cannot override a final method + IO.println("You have been eaten by a shark"); + } +} + +void main() { + BodyOfWater pacific = new Ocean("Pacific", 36201L); + pacific.swim(); +} +``` + +Another way to prevent this is to make it so the method is not visible to the subclass. +This can be done either by marking the method `private` or by having it be package-private +and simply in a different package. + +```java,does_not_compile +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + // Cannot be override what you cannot see + private void swim() { + if (depth > 50) { + IO.println("You have drowned"); + } + else { + IO.println("You are fine"); + } + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + super(depth); + } + + @Override + void swim() { // Cannot be override what you cannot see + IO.println("You have been eaten by a shark"); + } +} + +void main() { + BodyOfWater pacific = new Ocean("Pacific", 36201L); + pacific.swim(); +} +``` + +If you want to use the definition of the method you are overriding you can access it by writing `super.` +before the name of the method. + +```java +class BodyOfWater { + long depth; + + BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } + + void swim() { + if (depth > 50) { + IO.println("You have drowned"); + } + else { + IO.println("You are fine"); + } + } +} + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + super(depth); + } + + @Override + void swim() { + if ("Pacific".equals(name)) { + IO.println("You have been eaten by a shark"); + } + else { + // Calls the definition on BodyOfWater + super.swim(); + } + } +} + +void main() { + BodyOfWater pacific = new Ocean("Pacific", 36201L); + pacific.swim(); + BodyOfWater atlantic = new Ocean("Atlantic", 28232L); + atlantic.swim(); +} +``` \ No newline at end of file diff --git a/src/class_extension/protected.md b/src/class_extension/protected.md new file mode 100644 index 0000000..9bca086 --- /dev/null +++ b/src/class_extension/protected.md @@ -0,0 +1,44 @@ +# Protected + +If you want code to be accessible by a subclass defined in another package +but not be fully `public`, that is where the `protected` modifier is useful. + +`protected` fields, methods, and constructors have the same visibility as package-private ones. +This means classes in the same package can see them but classes in other packages cannot. +The only addition is that it is visible to extending classes regardless of package. + +```java +package oceanography; + +class BodyOfWater { + // Both the depth field and the following + // constructor are only visible to subclasses + // and classes in the "oceanography" package. + protected long depth; + + protected BodyOfWater(long depth) { + if (this.depth < 0) { + throw new IllegalArgumentException(); + } + this.depth = depth; + } +} +``` + +```java +package bigwater; + +class Ocean extends BodyOfWater { + String name; + + Ocean(String name, long depth) { + this.name = name; + // The super(depth) constructor is only usable because it is + // visible. + super(depth); + } +} +``` + +This is useful for providing methods that you really only expect to be useful for +producing subclasses but that you don't want to be available to everyone. \ No newline at end of file diff --git a/src/class_extension/relation_to_encapsulation.md b/src/class_extension/relation_to_encapsulation.md new file mode 100644 index 0000000..6faa05a --- /dev/null +++ b/src/class_extension/relation_to_encapsulation.md @@ -0,0 +1,141 @@ +# Relation to Encapsulation + +It takes deliberate effort to properly encapsulate implementation details +in the presence of inheritance. + +Say there was a class which tracked passengers who booked flights. + +```java +class Airplane { + private final List passengers = new ArrayList<>(); + + void bookOne(String passenger) { + this.passengers.add(passenger); + } + + void bookMany(List passengers) { + for (var passenger : passengers) { + bookOne(passenger); + } + } +} +``` + +A reasonable extension to this class would be to print a message every time someone books a flight.[^well] + +```java +class Airplane { + private final List passengers = new ArrayList<>(); + + void bookOne(String passenger) { + this.passengers.add(passenger); + } + + void bookMany(List passengers) { + for (var passenger : passengers) { + bookOne(passenger); + } + } +} + +class PrintingAirplane extends Airplane { + @Override + void bookOne(String passenger) { + IO.println(passenger); + super.bookOne(passenger); + } +} + +class Main { + void main() { + var airplane = new PrintingAirplane(); + airplane.bookOne("Ned Ludd"); + airplane.bookMany(List.of("Robin Hood", "Friar Tuck")); + } +} +``` + +But a subtle problem with this is that in Airplane `bookMany` is defined in terms of `bookOne`. +So in our subclass when we override `bookOne` we get the behavior we want. Every time someone is booked +we print their name. + +But a small innocuous change in `Airplane` could break this. If `bookMany` was redefined to not +use `bookOne` then you would miss names. + +```java +class Airplane { + private final List passengers = new ArrayList<>(); + + void bookOne(String passenger) { + this.passengers.add(passenger); + } + + void bookMany(List passengers) { + for (var passenger : passengers) { + bookOne(passenger); + } + } +} +``` + +A reasonable extension to this class would be to print a message every time someone books a flight.[^well] + +```java +class Airplane { + private final List passengers = new ArrayList<>(); + + void bookOne(String passenger) { + this.passengers.add(passenger); + } + + void bookMany(List passengers) { + for (var passenger : passengers) { + // Only change + this.passengers.add(passenger); + } + } +} + +class PrintingAirplane extends Airplane { + @Override + void bookOne(String passenger) { + IO.println(passenger); + super.bookOne(passenger); + } +} + +class Main { + void main() { + var airplane = new PrintingAirplane(); + airplane.bookOne("Ned Ludd"); + airplane.bookMany(List.of("Robin Hood", "Friar Tuck")); + } +} +``` + +This can be adjusted for by overriding both `bookOne` and `bookMany` in `PrintingAirplane`, but you are equally vulnerable to the opposite change. + +```java +class PrintingAirplane extends Airplane { + @Override + void bookOne(String passenger) { + IO.println(passenger); + super.bookOne(passenger); + } + + @Override + void bookMany(List passengers) { + for (var passenger : passengers) { + IO.println(passenger); + } + super.bookMany(passengers); + } +} +``` + +As a general rule, changing a class which may have been extended by other classes requires more effort than if doing so were not an option. This is because in a few ways "inheritance breaks encapsulation." +At least in the sense of there being an interplay between mechanics you wouldn't otherwise have to consider. + +If you don't want to deal with these issues, you can disallow extending a class. + +[^well]: Well, reasonable in the world of contrived programming examples. \ No newline at end of file diff --git a/src/class_extension/relation_to_interfaces.md b/src/class_extension/relation_to_interfaces.md new file mode 100644 index 0000000..00ae02c --- /dev/null +++ b/src/class_extension/relation_to_interfaces.md @@ -0,0 +1,83 @@ +# Relation to Interfaces + +When you implement an interface, the implementing class guarentees a contract +and may inherit some default implementations of methods. + +```java +// All implementing classes must define a run method +interface Runner { + void run(); + + default void runFar() { + run(); + run(); + run(); + } +} + +class RoadRunner { + @Override + public void run() { + IO.println("meep meep"); + } +} + +class Main { + void main() { + Runner r = new RoadRunner(); + r.runFar(); + } +} +``` + +This is also true when extending a class. The difference is that you may inherit +fields, behavior dependent on fields, and actual constructors. + +```java +abstract class Swimmer { + private int laps = 0; + + public abstract void swim(); + + // The inherited behavior may depend on fields + public void swimFar() { + IO.println("lap: " + laps); + swim(); + laps++; + + IO.println("lap: " + laps); + swim(); + laps++; + + IO.println("lap: " + laps); + swim(); + laps++; + } +} + +class Person extends Swimmer { + @Override + public void swim() { + IO.println("*gasps for air*"); + } +} + +class Main { + void main() { + Swimmer s = new Person(); + s.swimFar(); + } +} +``` + +Because of these differences while it is possible to implement many interfaces, a +class can only extend from exactly one other class.[^somelangs] + +Because of these differences, the general consensus is that interfaces are the mechanism to use +for abstracting behavior. Abstract classes, by contrast, should be used primarily to reduce duplicated code.[^tricky] + +[^somelangs]: Some languages do allow for "multiple inheritance." Doing so leads to some wacky and hard to think about things so Java doesn't allow it. + +[^tricky]: It's a little tricky because both interfaces and abstract classes can be used for similar things. A "pure abstract class" - a class with nothing but a bunch of abstract methods - is very close in +concept to an interface. If its all still confusing to you then just ignore everything but interfaces. + diff --git a/src/classes/aliasing.md b/src/classes/aliasing.md index 5619569..75baf72 100644 --- a/src/classes/aliasing.md +++ b/src/classes/aliasing.md @@ -19,8 +19,8 @@ void main() { kermit.name = "Kermit The Frog"; // Kermit The Frog - System.out.println(kermit.name); + IO.println(kermit.name); // Kermit The Frog - System.out.println(darkKermit.name); + IO.println(darkKermit.name); } ``` \ No newline at end of file diff --git a/src/classes/challenges.md b/src/classes/challenges.md index 94fbdee..61e4641 100644 --- a/src/classes/challenges.md +++ b/src/classes/challenges.md @@ -17,9 +17,9 @@ class fozzie_the_bear {} class MSPIGGY {} void main() { - System.out.println(new gonzo()); - System.out.println(new fozzie_the_bear()); - System.out.println(new MSPIGGY()); + IO.println(new gonzo()); + IO.println(new fozzie_the_bear()); + IO.println(new MSPIGGY()); } ``` @@ -38,7 +38,7 @@ void main() { // CODE HERE // --------------- - System.out.println( + IO.println( movie.title ); } @@ -57,7 +57,7 @@ class ThemePark { void main() { ThemePark themePark = new ThemePark(); - System.out.println( + IO.println( themePark.entranceFee ); } @@ -82,7 +82,7 @@ void main() { Kermit kermit = new Kermit(); // ------------------------ - System.out.println(kermit.angry); + IO.println(kermit.angry); } ``` @@ -114,15 +114,15 @@ SquareRoot squareRoot(double value) { void main() { SquareRoot sqrtOfFour = squareRoot(4); // 2 - System.out.println(sqrtOfFour.positiveRoot); + IO.println(sqrtOfFour.positiveRoot); // -2 - System.out.println(sqrtOfFour.negativeRoot); + IO.println(sqrtOfFour.negativeRoot); SquareRoot sqrtOfFifteen = squareRoot(15); // 3.872983346207417 - System.out.println(sqrtOfFifteen.positiveRoot); + IO.println(sqrtOfFifteen.positiveRoot); // -3.872983346207417 - System.out.println(sqrtOfFifteen.negativeRoot); + IO.println(sqrtOfFifteen.negativeRoot); } ``` @@ -145,6 +145,6 @@ void main() { // CODE HERE // -------------------------- - System.out.println(actor.name); + IO.println(actor.name); } ``` \ No newline at end of file diff --git a/src/classes/field_access.md b/src/classes/field_access.md index e7975fe..0986593 100644 --- a/src/classes/field_access.md +++ b/src/classes/field_access.md @@ -13,6 +13,6 @@ void main() { kermit.name = "Kermit The Frog"; // The .name accesses the "name" field - System.out.println(kermit.name); + IO.println(kermit.name); } ``` diff --git a/src/classes/field_default_values.md b/src/classes/field_default_values.md index c80d269..ed3f042 100644 --- a/src/classes/field_default_values.md +++ b/src/classes/field_default_values.md @@ -17,12 +17,12 @@ void main() { Muppet kermit = new Muppet(); // 0 - System.out.println(kermit.age); + IO.println(kermit.age); // 0.0 - System.out.println(kermit.salary); + IO.println(kermit.salary); // false - System.out.println(kermit.talented); + IO.println(kermit.talented); // null - System.out.println(kermit.name); + IO.println(kermit.name); } ``` \ No newline at end of file diff --git a/src/classes/instances.md b/src/classes/instances.md index 2737938..6ebcce5 100644 --- a/src/classes/instances.md +++ b/src/classes/instances.md @@ -11,7 +11,7 @@ class Muppet { void main() { Muppet kermit = new Muppet(); - System.out.println(kermit); + IO.println(kermit); } ``` diff --git a/src/classes/return_multiple_values.md b/src/classes/return_multiple_values.md index 7db4f01..1d422e2 100644 --- a/src/classes/return_multiple_values.md +++ b/src/classes/return_multiple_values.md @@ -22,7 +22,7 @@ Location findTreasureIsland() { void main() { Location treasureIsland = findTreasureIsland(); - System.out.println( + IO.println( "Treasure island is located at " + treasureIsland.latitude + " " + diff --git a/src/classes/the_meaning_of_the_word_class.md b/src/classes/the_meaning_of_the_word_class.md index 2e42e04..a6f036c 100644 --- a/src/classes/the_meaning_of_the_word_class.md +++ b/src/classes/the_meaning_of_the_word_class.md @@ -11,5 +11,5 @@ it stores. The `String` class would specify all of that.[^heady] [^idiot]: What an [idiot](https://www.shardcore.org/spx/2015/05/15/diogenes-and-the-chicken/) -[^heady]: If thats a bit too heady of an explanation don't fret. You will likely get it intuitively eventually. +[^heady]: If that's a bit too heady of an explanation don't fret. You will likely get it intuitively eventually. There are like 50 different ways to explain it and eventually one will land for you. \ No newline at end of file diff --git a/src/collections.md b/src/collections.md new file mode 100644 index 0000000..f1d7d23 --- /dev/null +++ b/src/collections.md @@ -0,0 +1,11 @@ +# Collections + +Arrays, `ArrayList`, and `HashMap` are all "collections" +of objects. Importantly, they also aren't the only possible +kinds of collections. + +Java provides support for a wide range of different collection types +through "The Collections Framework."[^marketing] + +[^marketing]: There is nothing really special about these interfaces and classes that mean they deserve such a name. I view it as a marketing term. There was a period of time before which Java did not have `ArrayList`, `HashMap`, etc. and when they were added they said "this is the new collections framework" +instead of "we've added these dozen classes." \ No newline at end of file diff --git a/src/collections/arrays.md b/src/collections/arrays.md new file mode 100644 index 0000000..74df3d3 --- /dev/null +++ b/src/collections/arrays.md @@ -0,0 +1,72 @@ +# Arrays + +Arrays are the odd duck out in the world of collections. They are basically a `List` but aren't `List`s.[^history] + +You can make a `List` which is a view over an array with `Arrays.asList`. + +```java +import java.util.List; +import java.util.Arrays; + +class Main { + void main() { + String[] furniture = new String[] { + "Ottoman", + "Table", + "Dresser" + }; + + List furnitureList = Arrays.asList(furniture); + + IO.println(furnitureList); + } +} +``` + +Changes made to the `List` returned from `Arrays.asList` will be reflected in the underlying array. + +```java +import java.util.List; +import java.util.Arrays; + +class Main { + void main() { + String[] furniture = new String[] { + "Ottoman", + "Table", + "Dresser" + }; + + List furnitureList = Arrays.asList(furniture); + + furnitureList.set(0, "Recliner"); + + IO.println(Arrays.toString(furniture)); + } +} +``` + +Accordingly, any methods on `List` which try to perform operations that an array cannot support (such as `.add`) will throw exceptions. + +```java,panics +import java.util.List; +import java.util.Arrays; + +class Main { + void main() { + String[] furniture = new String[] { + "Ottoman", + "Table", + "Dresser" + }; + + List furnitureList = Arrays.asList(furniture); + + // Cannot add to an array! + furnitureList.add("Shelf"); + } +} +``` + +[^history]: Arrays are unique beasts. This is true both in Java the language and in the virtual machine Java code runs on. +This is partially attributable to arrays coming first in the history - `List` and friends were not in the first version of Java. \ No newline at end of file diff --git a/src/collections/collection.md b/src/collections/collection.md new file mode 100644 index 0000000..e9e3a5f --- /dev/null +++ b/src/collections/collection.md @@ -0,0 +1 @@ +# Collection diff --git a/src/collections/factories.md b/src/collections/factories.md new file mode 100644 index 0000000..4523db5 --- /dev/null +++ b/src/collections/factories.md @@ -0,0 +1,88 @@ +# Factories + +There are methods on `List`, `Set`, and `Map`[^interface_static_methods] which can give +you instances of their corresponding collection. + +```java +import java.util.List; +import java.util.Set; +import java.util.Map; + +class Main { + void main() { + List weapons = List.of("Lightsaber", "Blaster"); + Set ships = Set.of("Tie Fighter", "X-Wing"); + Map midichlorians = Map.of( + "Anakin", 27000, + "Jar-Jar Binks", 0 + ); + + IO.println(weapons); + IO.println(ships); + IO.println(midichlorians); + } +} +``` + +The collections returned by these `of` methods are immutable. This means methods +which would change the underlying collection will throw an `UnsupportedOperationException`.[^fine] + +```java,panics +import java.util.List; + +class Main { + void main() { + List weapons = List.of("Lightsaber", "Blaster"); + + // Unsupported + weapons.add("A winning smile?") + + IO.println(weapons); + } +} +``` + +If you want the convenience of the factory methods but actually want an `ArrayList`, `HashMap`, or +a similar collection which supports `.add`, `.remove`, etc. you are in luck. Those classes generally +have a constructor which will copy another `List`, `Map`, or `Set`. + +```java +import java.util.List; + +class Main { + void main() { + // Reads better than a bunch of .add calls + List weapons = new ArrayList<>(List.of("Lightsaber", "Blaster")); + + // Will work! + weapons.add("A winning smile?") + + IO.println(weapons); + } +} +``` + +If you want the opposite - if you want to make a copy of a something like an `ArrayList` +which does not support `.add`, `.remove`, etc. - you can use `copyOf`. + +```java +import java.util.List; + +class Main { + void main() { + List weapons = new ArrayList<>(List.of("Lightsaber", "Blaster")); + weapons.add("A winning smile?") + IO.println(weapons); + + // Similar methods exist for Map and Set + List unchangable = List.copyOf(weapons); + IO.println(unchangable); + } +} +``` + +[^interface_static_methods]: Interfaces can have static methods. We'll cover it in a bit. For now all you need to know is that these methods exist, not how to define similar ones yourself. + +[^fine]: This is often fine. When something doesn't change after construction its one less thing to +have to think about when reading code. If you pass an `ArrayList` to a method you do need to wonder +if it is only going to be read or if something that you forgot about will call `.add`, `.remove`, etc. \ No newline at end of file diff --git a/src/collections/list.md b/src/collections/list.md new file mode 100644 index 0000000..2de9d21 --- /dev/null +++ b/src/collections/list.md @@ -0,0 +1,57 @@ +# List + +A `List` is an ordered collection of elements that generally allows duplicates. +Lists that come with Java implement the `java.util.List` interface +which provides myriad methods for working with such a collection. + +```java,no_run +interface List { + int size(); + boolean isEmpty(); + E get(int index); + // and more +} +``` + +`ArrayList` implements `List` and is likely the named class you will see +most often, but there are other implementations you will encounter as time goes on. + +```java +import java.util.List; +import java.util.ArrayList; + +class Main { + void main() { + List names = new ArrayList<>(); + names.add("Andor"); + names.add("Bix"); + names.add("Luthen"); + + IO.println(names); + } +} +``` + +Every method and capability that `ArrayList` has is available from the `List` interface.[^saveafew] +This includes being able to be used in for-each loops. + +```java +import java.util.List; +import java.util.ArrayList; + +class Main { + void main() { + List names = new ArrayList<>(); + names.add("Andor"); + names.add("Bix"); + names.add("Luthen"); + + for (var name : names) { + IO.println(name); + } + } +} +``` + +[^saveafew]: Save a really niche one + diff --git a/src/collections/map.md b/src/collections/map.md new file mode 100644 index 0000000..3a3b8a9 --- /dev/null +++ b/src/collections/map.md @@ -0,0 +1,33 @@ +# Map + +A `Map` is a collection that maps keys to values. Maps that come with +Java implement the `java.util.Map` interface. + +```java +interface Map { + V get(Object key); + V put(K key, V value); + // and more +} +``` + +`HashMap` is one implementation of `Map` you are likely to see and use. + +```java +import java.util.Map; +import java.util.HashMap; + +class Main { + void main() { + Map ages = new HashMap<>(); + ages.put("Andor", 26); + ages.put("Bix", 27); + ages.put("Luthen", 59); + + IO.println(ages); + } +} +``` + +Just like `ArrayList` and `List`, all the useful capabilities of `HashMap` are available +via the `Map` interface. \ No newline at end of file diff --git a/src/collections/set.md b/src/collections/set.md new file mode 100644 index 0000000..d101073 --- /dev/null +++ b/src/collections/set.md @@ -0,0 +1,39 @@ +# Set + +A `Set` is a collection of elements that does not allow duplicates. +Sets that come with Java implement the `java.util.Set` interface. + +```java,no_run +interface Set { + boolean contains(Object o); + boolean add(E e); + boolean remove(Object o); + // and more +} +``` + +`HashSet` provides an implementation of `Set` that works via the same mechanics - `.hashCode` and `.equals` - as a `HashMap`. + +```java +import java.util.Set; +import java.util.HashSet; + +class Main { + void main() { + Set agents = new HashSet<>(); + agents.add("Syril"); + agents.add("Dedra"); + + IO.println(agents); + + // If you try to add a duplicate to a set it won't + // crash, but there won't be two in the collection + agents.add("Syril"); + + IO.println(agents); + } +} +``` + +Checking membership in a large set is generally fast for the same reasons adding a new element +to a large map is fast. \ No newline at end of file diff --git a/src/collections/specificity.md b/src/collections/specificity.md new file mode 100644 index 0000000..7638d22 --- /dev/null +++ b/src/collections/specificity.md @@ -0,0 +1,30 @@ +# Specificity + +When you write methods that accept or return collections, the social rule is to +use the interface, not a specific collection type. + +```java,no_run +// You are generally expected to write this +List process(Set elements) { + // ... +} + +// And not this +ArrayList process(HashSet elements) { + // ... +} +``` + +There are valid reasons for this. + +* When you take a collection as an argument +the code that follows probably would work for any implementation of that collection. Why make someone +use any specific one? +* When you return a collection it is possible you might want to change the exact kind of +collection you return later. Why make things harder for future you? + +But there is also a dimension to it which is similar to how we choose to name classes, variables, and +methods. + +Socially it will cause you trouble if you make something which works in terms of a "concrete" +collection. Writing `List` instead of `ArrayList` where possible doesn't make your programs worse and it avoids frustrating interactions. \ No newline at end of file diff --git a/src/collections/unsupported_operation_exception.md b/src/collections/unsupported_operation_exception.md new file mode 100644 index 0000000..ffd7c62 --- /dev/null +++ b/src/collections/unsupported_operation_exception.md @@ -0,0 +1,34 @@ +# UnsupportedOperationException + +`List`, `Map`, and `Set` are interfaces with a lot of methods. +There are circumstances - like a `List` directly wrapping an array - +where it isn't possible to provide an implementation +for all those methods. + +In these situations, the specific exception that will be thrown is an `UnsupportedOperationException`. + +```java +import java.util.List; +import java.util.Arrays; + +class Main { + void main() { + String[] furniture = new String[] { + "Ottoman", + "Table", + "Dresser" + }; + + List furnitureList = Arrays.asList(furniture); + + try { + furnitureList.add("Shelf"); + } catch (UnsupportedOperationException e) { + IO.println("Tried to do an unsupported operation"); + } + } +} +``` + +For each collection some operations are mandatory to implement - like `.size` on `List` - +while others are allowed to be unsupported and still be a "valid" implementation. \ No newline at end of file diff --git a/src/collections_ii.md b/src/collections_ii.md new file mode 100644 index 0000000..da6ed7d --- /dev/null +++ b/src/collections_ii.md @@ -0,0 +1 @@ +# Collections II diff --git a/src/collections_ii/abstract_list.md b/src/collections_ii/abstract_list.md new file mode 100644 index 0000000..93122f4 --- /dev/null +++ b/src/collections_ii/abstract_list.md @@ -0,0 +1 @@ +# AbstractList diff --git a/src/collections_ii/collection.md b/src/collections_ii/collection.md new file mode 100644 index 0000000..e9e3a5f --- /dev/null +++ b/src/collections_ii/collection.md @@ -0,0 +1 @@ +# Collection diff --git a/src/collections_ii/sequenced_collection.md b/src/collections_ii/sequenced_collection.md new file mode 100644 index 0000000..8db8959 --- /dev/null +++ b/src/collections_ii/sequenced_collection.md @@ -0,0 +1 @@ +# SequencedCollection diff --git a/src/command_line_arguments/accessing_arguments.md b/src/command_line_arguments/accessing_arguments.md index 9a1c251..67a281f 100644 --- a/src/command_line_arguments/accessing_arguments.md +++ b/src/command_line_arguments/accessing_arguments.md @@ -28,7 +28,7 @@ java src/Main.java Duck Squirrel ~class Main { void main(String[] args) { for (int i = 0; i < args.length; i++) { - System.out.println( + IO.println( "Hello Mr. " + args[i] ); } diff --git a/src/compilation.md b/src/compilation.md new file mode 100644 index 0000000..86216d0 --- /dev/null +++ b/src/compilation.md @@ -0,0 +1,5 @@ +# Compilation + +The first step to sharing code you've written with other people is to +compile it. + diff --git a/src/constructors/arguments.md b/src/constructors/arguments.md index 7a7073a..fb26d30 100644 --- a/src/constructors/arguments.md +++ b/src/constructors/arguments.md @@ -19,7 +19,7 @@ void main() { Muppet gonzo = new Muppet("Gonzo"); // "Gonzo" - System.out.println(gonzo.name); + IO.println(gonzo.name); } ``` diff --git a/src/constructors/challenges.md b/src/constructors/challenges.md index 9a8c32d..8d2295a 100644 --- a/src/constructors/challenges.md +++ b/src/constructors/challenges.md @@ -19,13 +19,13 @@ enum Quality { void main() { Shoe nike = new Shoe("Nikes", Quality.SUB_FINE); - System.out.println( + IO.println( "SHOE: " + nike.name + ", " + nike.quality ); Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); - System.out.println( + IO.println( "SHOE: " + moccasin.name + ", " + moccasin.quality ); } @@ -52,18 +52,18 @@ enum Quality { void main() { Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); - System.out.println( + IO.println( "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price ); Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); - System.out.println( + IO.println( "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price ); Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); - System.out.println( + IO.println( "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price ); } @@ -89,18 +89,18 @@ enum Quality { void main() { Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); - System.out.println( + IO.println( "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price ); Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); - System.out.println( + IO.println( "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price ); Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); - System.out.println( + IO.println( "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price ); @@ -127,18 +127,18 @@ enum Quality { void main() { Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); - System.out.println( + IO.println( "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price ); Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); - System.out.println( + IO.println( "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price ); Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); - System.out.println( + IO.println( "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price ); diff --git a/src/constructors/declaration.md b/src/constructors/declaration.md index 2ae1b36..27236a7 100644 --- a/src/constructors/declaration.md +++ b/src/constructors/declaration.md @@ -25,7 +25,7 @@ class Muppet { void main() { Muppet gonzo = new Muppet(); - System.out.println(gonzo.talented); + IO.println(gonzo.talented); } ``` diff --git a/src/constructors/final_fields.md b/src/constructors/final_fields.md index 6654227..9c24826 100644 --- a/src/constructors/final_fields.md +++ b/src/constructors/final_fields.md @@ -17,7 +17,7 @@ class Muppet { void main() { Muppet gonzo = new Muppet("Gonzo"); - System.out.println(gonzo.name); + IO.println(gonzo.name); // Cannot update the .name field later // gonzo.name = "Gonzo, the great"; @@ -34,7 +34,7 @@ class Muppet { void main() { Muppet gonzo = new Muppet(); - System.out.println(gonzo.talented); + IO.println(gonzo.talented); } ``` diff --git a/src/constructors/invariants.md b/src/constructors/invariants.md index c09a7bc..13b96a2 100644 --- a/src/constructors/invariants.md +++ b/src/constructors/invariants.md @@ -25,7 +25,7 @@ class Muppet { void main() { Muppet bigBird = new Muppet("Big Bird", 6); - System.out.println( + IO.println( bigBird.name + " is " + bigBird.age + " years old." ); } diff --git a/src/constructors/overloads.md b/src/constructors/overloads.md index 7ef5c4e..bb309ea 100644 --- a/src/constructors/overloads.md +++ b/src/constructors/overloads.md @@ -40,14 +40,14 @@ class Muppet { } void announce(Muppet muppet) { - System.out.print(muppet.name); + IO.print(muppet.name); if (muppet.talented) { - System.out.print(" is "); + IO.print(" is "); } else { - System.out.print(" is not "); + IO.print(" is not "); } - System.out.println("talented."); + IO.println("talented."); } void main() { diff --git a/src/constructors/the_default_constructor.md b/src/constructors/the_default_constructor.md index cd75169..45a8573 100644 --- a/src/constructors/the_default_constructor.md +++ b/src/constructors/the_default_constructor.md @@ -16,8 +16,8 @@ void main() { Muppet gonzo = new Muppet(); // null - System.out.println(gonzo.name); + IO.println(gonzo.name); // false - System.out.println(gonzo.talented); + IO.println(gonzo.talented); } ``` \ No newline at end of file diff --git a/src/documentation/documentation_comments.md b/src/documentation/documentation_comments.md new file mode 100644 index 0000000..d16a981 --- /dev/null +++ b/src/documentation/documentation_comments.md @@ -0,0 +1 @@ +# Documentation Comments diff --git a/src/encapsulation/abstractions.md b/src/encapsulation/abstractions.md index fa05de0..b7f2969 100644 --- a/src/encapsulation/abstractions.md +++ b/src/encapsulation/abstractions.md @@ -1,4 +1,4 @@ -# Abstractions +# Abstraction A term closely related to encapsulation is abstraction. diff --git a/src/encapsulation/classes.md b/src/encapsulation/classes.md index 87846ef..7c4350d 100644 --- a/src/encapsulation/classes.md +++ b/src/encapsulation/classes.md @@ -1 +1,64 @@ # Classes + +One step above methods is classes. + +When classes have private fields and all interactions with those fields +are mediated through methods + +The `ArrayList` class that comes with Java looks something like this.[^liberties] + +```java +public class ArrayList extends AbstractList + implements List, RandomAccess, Cloneable, java.io.Serializable +{ + Object[] elementData; + + private int size; + + public ArrayList() { + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; + } + + private void add(E e, Object[] elementData, int s) { + if (s == elementData.length) + elementData = grow(); + elementData[s] = e; + size = s + 1; + } + + private Object[] grow(int minCapacity) { + int oldCapacity = elementData.length; + if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + int newCapacity = ArraysSupport.newLength(oldCapacity, + minCapacity - oldCapacity, /* minimum growth */ + oldCapacity >> 1 /* preferred growth */); + return elementData = Arrays.copyOf(elementData, newCapacity); + } else { + return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; + } + } + + private Object[] grow() { + return grow(size + 1); + } + + public boolean add(E e) { + modCount++; + add(e, elementData, size); + return true; + } + + // ... +} +``` + +People who write programs that depend on the calling `.add` on an `ArrayList` do not need +to understand how or when the internal `elementData` and `size` fields are updated. Those also +need not be the only fields that exist. All that is required is "calling `.add` will add an element to the list." + +It is in this way that classes provide encapsulation. They can hide the fields which +are needed and the way in which they are used - "implementation details" - in order +to provide some implicit interface. + + +[^liberties]: Creative liberties taken. \ No newline at end of file diff --git a/src/encapsulation/coupling.md b/src/encapsulation/coupling.md new file mode 100644 index 0000000..82dc841 --- /dev/null +++ b/src/encapsulation/coupling.md @@ -0,0 +1,18 @@ +# Coupling + + + +If two things are "coupled" it means that a change in one +will mandate a change in the other. Or, at the very least, +active consideration of whether such a change is neccesary. + +In a way "coupling" is the opposite of "abstraction." Instead +of hiding implementation details you freely intermingle them. + +This does not mean that coupling is bad. On the contrary, so +long as code that is coupled is also "co-located" making changes +to that code can be easier than if it were spread over multiple files. + +A not insignificant part of practice in the coding world is deciding when +pieces of a program deserve to be abstracted from eachother (to minimize interdependence) +and when they deserve to be coupled together (to keep logic in one place.) \ No newline at end of file diff --git a/src/encapsulation/implicit_interfaces.md b/src/encapsulation/implicit_interfaces.md index 837c75e..98660db 100644 --- a/src/encapsulation/implicit_interfaces.md +++ b/src/encapsulation/implicit_interfaces.md @@ -1 +1,20 @@ # Implicit Interfaces + +There are many kinds of "interfaces." + +There are `interface`s the Java language construct - these let you +specify methods that different classes will share and write code +that will work against anything which implements that interface. + +But there are also "interfaces" that you encounter in the real world. +The classic example is an automobile. Once you understand +the "interface" of a steering wheel, brake pedal, and gas pedal +you can drive most any car. + +Then back within Java there are things which form "an interface" +which may or may not use the `interface` keyword. The static methods available on a class, +the constructors that are public, the set of classes that come with a library, etc. + +I think a more general concept "the implicit interface." Everything you can observe about a thing forms its "implicit interface."[^implicit] + +[^implicit]: I call it "implicit" because there is no place where you write down "all the properties of a thing" that is not the thing itself. And by thing I mean field, method, class, group of classes, package names - everything. \ No newline at end of file diff --git a/src/encapsulation/information_hiding.md b/src/encapsulation/information_hiding.md index e01dcd9..b0e2e05 100644 --- a/src/encapsulation/information_hiding.md +++ b/src/encapsulation/information_hiding.md @@ -6,11 +6,95 @@ implementation details. If you did not hide this information and you have a large number of consumers[^if], you would never -be able to change those implementation details. +be able to change those implementation details. Your consumers +can be coupled to them. Something to be careful of with respect to this is "side channels." -If you use the mechanisms Java gives you to hide a field from people, -.. +If you use the mechanisms Java gives you to hide a field from people +you can get pretty far, but you still need to be wary of accidentally allowing +people to do things you don't want. + +```java,no_run +import java.util.ArrayList; + +class Total { + private final ArrayList nums; + + Total() { + this.nums = new ArrayList<>(); + } + + void add(int num) { + nums.add(num); + } + + int total() { + int sum = 0; + for (int num : nums) { + sum += num; + } + return sum; + } + + @Override + public String toString() { + return "Total[" + nums + "]"; + } +} +``` + +In the code above the `Total` class hides a field holding an `ArrayList` +and just exposes the total of the numbers added to it. + +```java,no_run +void main() { + var total = new Total(); + total.add(3); + total.add(2); + total.add(1); + + IO.println(total.total()); +} +``` + +This lets it later on decide to maybe not store the list of numbers at all and instead keep a running total. + +```java,no_run +import java.util.ArrayList; + +class Total { + private int runningTotal; + + Total() { + this.runningTotal = 0; + } + + void add(int num) { + runningTotal += num; + } + + int total() { + return runningTotal; + } + + @Override + public String toString() { + return "Total[" + runningTotal + "]"; + } +} +``` + +The problem is that if you had called `.toString()` on the previous implementation +you might have gotten a `String` that looks like `Total[[3, 2, 1]]`. The information +of what numbers are in the list was exposed there. + +With enough people using this code some motivated moron will work backwards +from that `String` representation to get at the underlying list. Now if you changed the implementation +you would break their code. + +We would call that a "side channel." The information is exposed, just through an API you didn't consider. +How you handle that is heavily dependent on the exact situation, but its a category of issue you should know about. + [^if]: Again, if. These concerns do not apply as much to programs written at smaller scales or programs written diff --git a/src/encapsulation/leaky_abstractions.md b/src/encapsulation/leaky_abstractions.md index f00d449..6cd6961 100644 --- a/src/encapsulation/leaky_abstractions.md +++ b/src/encapsulation/leaky_abstractions.md @@ -1 +1,14 @@ # Leaky Abstractions + +If an abstraction doesn't fully encapsulate what it is trying to we call +that abstraction "leaky." + +As an example, say we define a person as having a first name and a given name. That +might work for a large number of people. But the moment our program needs to represent +Plato, who did not have a given name, that abstraction "leaks." + +Now suddenly the code that would work with people needs to account for an "empty" given name +where it didn't before and we need to pick a special value to represent such a name (empty string or `null` generally). The abstraction has leaked. + +There are maybe more illuminating examples, but thats the general concept. Its not the end of the +world if an abstraction leaks. It might just be a sign of a changing world or of not having thought through a problem fully. You can adapt to that. It's just worth knowing about. \ No newline at end of file diff --git a/src/enums/challenges.md b/src/enums/challenges.md index 34fd123..b892882 100644 --- a/src/enums/challenges.md +++ b/src/enums/challenges.md @@ -16,15 +16,15 @@ has three variants. `YES`, `NO`, and `MAYBE_SO`. // ------------- void main() { - System.out.println( + IO.println( Response.YES ); - System.out.println( + IO.println( Response.NO ); - System.out.println( + IO.println( Response.MAYBE_SO ); } @@ -48,16 +48,16 @@ Use the enum you defined above. void main() { Response pitbull = goodPerformer("Pitbull"); - System.out.println(pitbull); + IO.println(pitbull); Response billyJoel = goodPerformer("Billy Joel"); - System.out.println(billyJoel); + IO.println(billyJoel); Response shaggy = goodPerformer("Shaggy"); - System.out.println(shaggy); + IO.println(shaggy); Response chappelRoan = goodPerformer("Chappell Roan"); - System.out.println(chappelRoan); + IO.println(chappelRoan); } ``` @@ -84,24 +84,24 @@ StopLight transition(StopLight current) { void main() { var light = StopLight.RED; - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); } ``` \ No newline at end of file diff --git a/src/enums/comparison_to_boolean.md b/src/enums/comparison_to_boolean.md index c889bd7..da13906 100644 --- a/src/enums/comparison_to_boolean.md +++ b/src/enums/comparison_to_boolean.md @@ -21,10 +21,10 @@ void main() { Power power = Power.ON; if (power == Power.ON) { - System.out.println("The power is on"); + IO.println("The power is on"); } else { - System.out.println("The power is off"); + IO.println("The power is off"); } } ``` diff --git a/src/enums/equality.md b/src/enums/equality.md index 23b4375..84f4da8 100644 --- a/src/enums/equality.md +++ b/src/enums/equality.md @@ -14,10 +14,10 @@ void main() { StopLight light = StopLight.RED; if (light == StopLight.RED) { - System.out.println("You must stop"); + IO.println("You must stop"); } else { - System.out.println("Full speed ahead!"); + IO.println("Full speed ahead!"); } } ``` \ No newline at end of file diff --git a/src/enums/switch.md b/src/enums/switch.md index 5e103c9..9749d92 100644 --- a/src/enums/switch.md +++ b/src/enums/switch.md @@ -11,10 +11,10 @@ void main() { StopLight light = StopLight.RED; if (light == StopLight.RED) { - System.out.println("You must stop"); + IO.println("You must stop"); } else { - System.out.println("Full speed ahead!"); + IO.println("Full speed ahead!"); } } ``` \ No newline at end of file diff --git a/src/enums/usage.md b/src/enums/usage.md index 51f91eb..3dffd1c 100644 --- a/src/enums/usage.md +++ b/src/enums/usage.md @@ -16,7 +16,7 @@ enum StopLight { void main() { StopLight light = StopLight.RED; - System.out.println( + IO.println( "The light is " + light ); } diff --git a/src/exceptions.md b/src/exceptions.md index b37b472..9055789 100644 --- a/src/exceptions.md +++ b/src/exceptions.md @@ -11,7 +11,7 @@ In "exceptional conditions" code will no longer be able to proceed. ```java,panics void main() { int x = 5 / 0; - System.out.println("Won't get here, an exception will occur"); + IO.println("Won't get here, an exception will occur"); } ``` diff --git a/src/exceptions/messages.md b/src/exceptions/messages.md index 7364aa6..2785b06 100644 --- a/src/exceptions/messages.md +++ b/src/exceptions/messages.md @@ -15,9 +15,9 @@ void crashesOnFive(int x) { void main() { crashesOnFive(1); - System.out.println("Made it to step 1"); + IO.println("Made it to step 1"); crashesOnFive(5); - System.out.println("Will not make it to step 2"); + IO.println("Will not make it to step 2"); } ``` \ No newline at end of file diff --git a/src/exceptions/throw.md b/src/exceptions/throw.md index 7dabe91..d90dc91 100644 --- a/src/exceptions/throw.md +++ b/src/exceptions/throw.md @@ -15,9 +15,9 @@ void crashesOnFive(int x) { void main() { crashesOnFive(1); - System.out.println("Made it to step 1"); + IO.println("Made it to step 1"); crashesOnFive(5); - System.out.println("Will not make it to step 2"); + IO.println("Will not make it to step 2"); } ``` \ No newline at end of file diff --git a/src/exceptions/try_catch.md b/src/exceptions/try_catch.md index f4f2e13..562d8fb 100644 --- a/src/exceptions/try_catch.md +++ b/src/exceptions/try_catch.md @@ -36,7 +36,7 @@ void main() { try { doThing(x); } catch (RuntimeException e) { - System.out.println("Something went wrong doing a thing."); + IO.println("Something went wrong doing a thing."); } } ``` @@ -46,7 +46,7 @@ Just as you cannot have an `else` without an `if`, you cannot have a `catch` wit ```java,does_not_compile void main() { catch (RuntimeException e) { - System.out.println("Hello"); + IO.println("Hello"); } } ``` @@ -56,7 +56,7 @@ Nor can you have a `try` without a `catch`.[^trywithresources] ```java,does_not_compile void main() { try { - System.out.println("Hello"); + IO.println("Hello"); } } ``` diff --git a/src/exceptions_ii/propagating_exceptions.md b/src/exceptions_ii/propagating_exceptions.md index 0a90bba..bebd84b 100644 --- a/src/exceptions_ii/propagating_exceptions.md +++ b/src/exceptions_ii/propagating_exceptions.md @@ -4,7 +4,7 @@ Say we started out with code like this. ```java void dream() { - System.out.println("Shin Godzilla's Jaw unhinging like a snake...") + IO.println("Shin Godzilla's Jaw unhinging like a snake...") } void sleep() { diff --git a/src/exceptions_ii/rethrowing_exceptions.md b/src/exceptions_ii/rethrowing_exceptions.md index 63089f2..c1c9835 100644 --- a/src/exceptions_ii/rethrowing_exceptions.md +++ b/src/exceptions_ii/rethrowing_exceptions.md @@ -15,7 +15,7 @@ void sleep() throws Exception { dream(); } catch (Exception e) { - System.out.println("Something went wrong while dreaming"); + IO.println("Something went wrong while dreaming"); throw e; } } diff --git a/src/files/challenges.md b/src/files/challenges.md index d255025..9d33aa6 100644 --- a/src/files/challenges.md +++ b/src/files/challenges.md @@ -63,9 +63,9 @@ class Main { people = load(path); - System.out.println(people[0]); - System.out.println(people[1]); - System.out.println(people[2]); + IO.println(people[0]); + IO.println(people[1]); + IO.println(people[2]); } diff --git a/src/files/paths.md b/src/files/paths.md index d0970b6..17fa2b8 100644 --- a/src/files/paths.md +++ b/src/files/paths.md @@ -14,7 +14,7 @@ class Main { void main() { Path pathToNotes = Path.of("notes.txt"); - System.out.println(pathToNotes); + IO.println(pathToNotes); } } ``` diff --git a/src/files/read_from_a_file.md b/src/files/read_from_a_file.md index fe17cbc..c47ae6a 100644 --- a/src/files/read_from_a_file.md +++ b/src/files/read_from_a_file.md @@ -23,7 +23,7 @@ class Main { Path tasksPath = Path.of("tasks.txt"); String tasks = Files.readString(tasksPath); - System.out.println(tasks); + IO.println(tasks); } } ``` @@ -47,7 +47,7 @@ class Main { throw new UncheckedIOException(e); } - System.out.println(tasks); + IO.println(tasks); } } ``` diff --git a/src/first_steps.md b/src/first_steps.md index 2a56d24..fb4f046 100644 --- a/src/first_steps.md +++ b/src/first_steps.md @@ -4,7 +4,7 @@ If you made it through the [Getting Started section](./getting_started/hello_wor ```java void main() { - System.out.println("Hello, World!"); + IO.println("Hello, World!"); } ``` @@ -26,21 +26,21 @@ So for all intents and purposes, this is the whole program. ```java ~void main() { -System.out.println("Hello, World!"); +IO.println("Hello, World!"); ~} ``` -This bit of magic here - `System.out.println` - is a "statement" that "prints" the text inside the `(` and `)` as well as a "new line" to the screen. +This bit of magic here - `IO.println` - is a "statement" that "prints" the text inside the `(` and `)` as well as a "new line" to the screen. **print** with new **l**i**n**e. -If you were to replace it with `System.out.print`, then the output would lack that new line. This makes the following program be functionally identical to the first. +If you were to replace it with `IO.print`, then the output would lack that new line. This makes the following program be functionally identical to the first. ```java ~void main() { -System.out.print("Hello, "); -System.out.print("World"); -System.out.println("!"); +IO.print("Hello, "); +IO.print("World"); +IO.println("!"); ~} ``` @@ -48,9 +48,9 @@ Which, when we add back `void main()`, looks like this. ```java void main() { - System.out.print("Hello, "); - System.out.print("World"); - System.out.println("!"); + IO.print("Hello, "); + IO.print("World"); + IO.println("!"); } ``` diff --git a/src/first_steps/challenges.md b/src/first_steps/challenges.md index 0584a1b..d22cb2e 100644 --- a/src/first_steps/challenges.md +++ b/src/first_steps/challenges.md @@ -23,14 +23,14 @@ What will this program output when run? Write down your guess and then try actua ```text void main() { - System.out.println("A"); - //System.out.println("B"); - System.out.println("C");// - System.out.println("D"); + IO.println("A"); + //IO.println("B"); + IO.println("C");// + IO.println("D"); /* - System.out.println("E"); - System.out.println("F");*/ - System.out.println("G"); + IO.println("E"); + IO.println("F");*/ + IO.println("G"); } ``` @@ -42,9 +42,9 @@ How could you use that error to figure out where you might have forgotten to put ```java,editable void main() { - System.out.println("Apple"); - System.out.println("Banana"); - System.out.println("Clementine"); - System.out.println("Durian"); + IO.println("Apple"); + IO.println("Banana"); + IO.println("Clementine"); + IO.println("Durian"); } ``` diff --git a/src/first_steps/comments.md b/src/first_steps/comments.md index 46b12a5..ee5b53d 100644 --- a/src/first_steps/comments.md +++ b/src/first_steps/comments.md @@ -9,7 +9,7 @@ words. ```java void main() { // This prints hello world! - System.out.println("Hello, World!"); + IO.println("Hello, World!"); } ``` @@ -20,9 +20,9 @@ If you put `//` in front of something that is "code" and not an English explanat ```java void main() { - System.out.println("Hello, World!"); + IO.println("Hello, World!"); // The line that prints out goodbye is "commented out" - // System.out.println("Goodbye!"); + // IO.println("Goodbye!"); } ``` @@ -50,7 +50,7 @@ void main() { so sweet and so cold */ - System.out.println("Hello, World!"); + IO.println("Hello, World!"); } ``` diff --git a/src/first_steps/formatting.md b/src/first_steps/formatting.md index 1bbbaf1..bbe6525 100644 --- a/src/first_steps/formatting.md +++ b/src/first_steps/formatting.md @@ -4,7 +4,7 @@ You may have noticed that after each `{` all the code that comes after it is "in ```java void main() { - System.out.println("Hello, World!"); + IO.println("Hello, World!"); } ``` @@ -18,7 +18,7 @@ This is easier to show than to explain in detail. Just try to make your code loo ```java void main() { - System.out.println("Hello, World!"); + IO.println("Hello, World!"); } ``` @@ -29,7 +29,7 @@ And not like this. ```java void main() { -System.out.println("Hello, World!");} +IO.println("Hello, World!");} ``` And keep in mind that this rule of thumb applies to every language construct that requires a `{` and `}` many of which I will introduce later. diff --git a/src/first_steps/semicolon.md b/src/first_steps/semicolon.md index 676991b..c52b918 100644 --- a/src/first_steps/semicolon.md +++ b/src/first_steps/semicolon.md @@ -4,7 +4,7 @@ The `;` at the end of each of those lines is a "semicolon". ```java ~void main(){ -System.out.print("Hello, "); // <-- this thing +IO.print("Hello, "); // <-- this thing // ^ ~} ``` @@ -15,7 +15,7 @@ more complicated than these examples. ```java ~void main(){ -System.out.print( +IO.print( "Hello, " ); ~} @@ -29,7 +29,7 @@ If you happen to have an extra semi-colon or two that is technically okay. It ju ```java ~void main() { -System.out.print( +IO.print( "Hello, " );; ~} @@ -39,7 +39,7 @@ Or even ```java ~void main() { -System.out.print( +IO.print( "Hello, " ); diff --git a/src/floating_point_numbers/addition.md b/src/floating_point_numbers/addition.md index d0e4e5a..e093163 100644 --- a/src/floating_point_numbers/addition.md +++ b/src/floating_point_numbers/addition.md @@ -8,7 +8,7 @@ double x = 5.1; // y will be 14.2 double y = x + 9.1; -System.out.println(y); +IO.println(y); ~} ``` @@ -22,7 +22,7 @@ Because of the previously mentioned inaccuracy, the results of additions might n // z will be 19.299999999999997 double z = x + y; -System.out.println(z); +IO.println(z); ~} ``` @@ -35,9 +35,9 @@ double y = 4.4; // z will be 9.4 double z = x + y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -51,8 +51,8 @@ double y = 4; // this will not work. The result of the expression is a double. int z = x + y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` diff --git a/src/floating_point_numbers/challenges.md b/src/floating_point_numbers/challenges.md index 11c313f..259b4aa 100644 --- a/src/floating_point_numbers/challenges.md +++ b/src/floating_point_numbers/challenges.md @@ -13,7 +13,7 @@ What will this program output when run? Write down your guess and then try runni void main() { double x = 5.1; double y = 2.4; - System.out.println(x + y); + IO.println(x + y); } ``` @@ -25,7 +25,7 @@ What will this program output when run? Write down your guess and then try runni void main() { double x = 5.1; double y = 2.1; - System.out.println(x - y); + IO.println(x - y); } ``` @@ -38,7 +38,7 @@ How can you make it give the "right" answer? ```java,editable void main() { double x = 5 / 2; - System.out.println(x); + IO.println(x); } ``` @@ -51,8 +51,8 @@ void main() { double resultOne = (int) 5.0 / 2 + 5.0 / 2; double resultTwo = (int) (5.0 / 2 + 5.0 / 2); - System.out.println(resultOne); - System.out.println(resultTwo); + IO.println(resultOne); + IO.println(resultTwo); } ``` @@ -86,8 +86,8 @@ void main() { double resultOne = ???; double resultTwo = ???; - System.out.println(resultOne); - System.out.println(resultTwo); + IO.println(resultOne); + IO.println(resultTwo); } ``` diff --git a/src/floating_point_numbers/comparison.md b/src/floating_point_numbers/comparison.md index 076c068..13213ef 100644 --- a/src/floating_point_numbers/comparison.md +++ b/src/floating_point_numbers/comparison.md @@ -11,8 +11,8 @@ double x = 1.5; double y = 0.2; // true -System.out.println(x > y); +IO.println(x > y); // false -System.out.println(x < y); +IO.println(x < y); ~} ``` diff --git a/src/floating_point_numbers/conversion_from_integers.md b/src/floating_point_numbers/conversion_from_integers.md index 54e3930..ff10c27 100644 --- a/src/floating_point_numbers/conversion_from_integers.md +++ b/src/floating_point_numbers/conversion_from_integers.md @@ -9,8 +9,8 @@ by Java when performing an assignment. int x = 5; double y = x; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` @@ -24,7 +24,7 @@ int y = 2; // integer division of 7 and 2 gives 3. double z = x / y; -System.out.println(z); +IO.println(z); ~} ``` @@ -39,6 +39,6 @@ int y = 2; // so the result will be 3.5. double z = (double) x / y; -System.out.println(z); +IO.println(z); ~} ``` diff --git a/src/floating_point_numbers/conversion_to_integers.md b/src/floating_point_numbers/conversion_to_integers.md index 57f9f3f..6d571ca 100644 --- a/src/floating_point_numbers/conversion_to_integers.md +++ b/src/floating_point_numbers/conversion_to_integers.md @@ -29,7 +29,7 @@ double x = 5.0; // will be 5 int y = (int) x; -System.out.println(y); +IO.println(y); ~} ``` @@ -42,9 +42,9 @@ int x = (int) 2.1; int y = (int) 2.5; int z = (int) 2.9; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -53,11 +53,11 @@ Any number that is too big to store in an `int` will be converted to the biggest ```java ~void main() { // 2147483647 -System.out.println((int) 4207483647.0); +IO.println((int) 4207483647.0); double positiveInfinity = 5.0 / 0.0; // 2147483647 -System.out.println((int) positiveInfinity); +IO.println((int) positiveInfinity); ~} ``` @@ -66,11 +66,11 @@ Any number that is too small to store in an `int` will be converted to the small ```java ~void main() { // -2147483648 -System.out.println((int) -9999999999.0); +IO.println((int) -9999999999.0); double negativeInfinity = -5.0 / 0.0; // -2147483648 -System.out.println((int) negativeInfinity); +IO.println((int) negativeInfinity); ~} ``` @@ -79,7 +79,7 @@ And `NaN` will be converted to zero. ```java ~void main() { double nan = 0.0 / 0.0; -System.out.println((int) nan); +IO.println((int) nan); ~} ``` diff --git a/src/floating_point_numbers/division.md b/src/floating_point_numbers/division.md index 7563a95..cd4c552 100644 --- a/src/floating_point_numbers/division.md +++ b/src/floating_point_numbers/division.md @@ -10,9 +10,9 @@ double y = x / 2; // z will be 1.3333333333333333 double z = y / 3; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` diff --git a/src/floating_point_numbers/equality.md b/src/floating_point_numbers/equality.md index fa72695..fbaf95b 100644 --- a/src/floating_point_numbers/equality.md +++ b/src/floating_point_numbers/equality.md @@ -9,7 +9,7 @@ double numberOfFingers = 10.0; boolean humanGenerated = numberOfToes == numberOfFingers; -System.out.println(humanGenerated); +IO.println(humanGenerated); ~} ``` @@ -25,7 +25,7 @@ double z = x + y; // this will be false. boolean doesWhatYouExpect = z == 0.3; -System.out.println(doesWhatYouExpect); +IO.println(doesWhatYouExpect); ~} ``` @@ -39,6 +39,6 @@ double y = 5.0; // will be true boolean fiveIsFive = x == y; -System.out.println(fiveIsFive); +IO.println(fiveIsFive); ~} ``` diff --git a/src/floating_point_numbers/multiplication.md b/src/floating_point_numbers/multiplication.md index 24c126e..5d6066a 100644 --- a/src/floating_point_numbers/multiplication.md +++ b/src/floating_point_numbers/multiplication.md @@ -10,9 +10,9 @@ double y = x * 9; // z will be 13.5 double z = y * 0.5; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -23,6 +23,6 @@ multiplication on doubles. So long as any number being used is a `double` the ov ~void main() { // a will be 3.0 double a = 1.5 * 2; -System.out.println(a); +IO.println(a); ~} ``` diff --git a/src/floating_point_numbers/nan.md b/src/floating_point_numbers/nan.md index cf22e18..fdd3f94 100644 --- a/src/floating_point_numbers/nan.md +++ b/src/floating_point_numbers/nan.md @@ -16,7 +16,7 @@ double nan = 0.0 / 0.0; // will be false boolean equalToItself = nan == nan; -System.out.println(equalToItself); +IO.println(equalToItself); ~} ``` @@ -28,7 +28,7 @@ System.out.println(equalToItself); // will be false boolean greaterThanItself = nan > nan; -System.out.println(greaterThanItself); +IO.println(greaterThanItself); ~} ``` @@ -40,7 +40,7 @@ System.out.println(greaterThanItself); // will be false boolean lessThanItself = nan < nan; -System.out.println(lessThanItself); +IO.println(lessThanItself); ~} ``` @@ -50,9 +50,9 @@ System.out.println(lessThanItself); ~void main() { ~double nan = 0.0 / 0.0; // will all be false -System.out.println(nan < 5); -System.out.println(nan > 5); -System.out.println(nan == 5); +IO.println(nan < 5); +IO.println(nan > 5); +IO.println(nan == 5); ~} ``` diff --git a/src/floating_point_numbers/positive_and_negative_infinity.md b/src/floating_point_numbers/positive_and_negative_infinity.md index d87468e..570c544 100644 --- a/src/floating_point_numbers/positive_and_negative_infinity.md +++ b/src/floating_point_numbers/positive_and_negative_infinity.md @@ -22,10 +22,10 @@ As you might expect, positive infinity is greater than any number and negative i ~double positiveInfinity = 1.0 / 0.0; ~double negativeInfinity = -1.0 / 0.0; // true -System.out.println(positiveInfinity > 99999999); +IO.println(positiveInfinity > 99999999); // true -System.out.println(negativeInfinity < -99999999); +IO.println(negativeInfinity < -99999999); ~} ``` @@ -38,9 +38,9 @@ Except for `NaN`, of course. double nan = 0.0 / 0.0; // false -System.out.println(positiveInfinity > nan); +IO.println(positiveInfinity > nan); // false -System.out.println(negativeInfinity < nan); +IO.println(negativeInfinity < nan); ~} ``` diff --git a/src/floating_point_numbers/shorthands_for_reassignment.md b/src/floating_point_numbers/shorthands_for_reassignment.md index 42ece4c..d57d574 100644 --- a/src/floating_point_numbers/shorthands_for_reassignment.md +++ b/src/floating_point_numbers/shorthands_for_reassignment.md @@ -6,30 +6,30 @@ All the same shorthands for reassignment work with `double`s the same as they do ~void main() { double x = 0.5; // 0.5 -System.out.println(x); +IO.println(x); x += 3; // 3.5 -System.out.println(x); +IO.println(x); x -= 1; // 2.5 -System.out.println(x); +IO.println(x); x++; // 3.5 -System.out.println(x); +IO.println(x); x--; // 2.5 -System.out.println(x); +IO.println(x); x *= 5; // 12.5 -System.out.println(x); +IO.println(x); x /= 2; // 6.25 -System.out.println(x); +IO.println(x); ~} ``` diff --git a/src/floating_point_numbers/square_root.md b/src/floating_point_numbers/square_root.md index dca6221..82f4956 100644 --- a/src/floating_point_numbers/square_root.md +++ b/src/floating_point_numbers/square_root.md @@ -11,7 +11,7 @@ double x = 4; double y = Math.sqrt(x); // This will output 2 -System.out.println(y); +IO.println(y); ~} ``` @@ -24,7 +24,7 @@ double y = 13; double z = Math.sqrt(9 * x + y); // This will output 7.615773105863909 -System.out.println(z); +IO.println(z); ~} ``` @@ -33,6 +33,6 @@ If you try to take the square root of a negative number, the result will be `NaN ```java ~void main() { // will output NaN -System.out.println(Math.sqrt(-5.2)); +IO.println(Math.sqrt(-5.2)); ~} ``` diff --git a/src/floating_point_numbers/subtraction.md b/src/floating_point_numbers/subtraction.md index 6f4df8c..3e087ac 100644 --- a/src/floating_point_numbers/subtraction.md +++ b/src/floating_point_numbers/subtraction.md @@ -8,8 +8,8 @@ double x = 5.1; // y will be 4.1 double y = x - 9.2; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` @@ -23,7 +23,7 @@ Because of the previously mentioned inaccuracy, the results of subtractions migh // z will be -4.199999999999999 double z = y - 0.1; -System.out.println(z); +IO.println(z); ~} ``` @@ -36,9 +36,9 @@ double y = 4.5; // z will be 0.5 double z = x - y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` diff --git a/src/generics/inference.md b/src/generics/inference.md index 659b409..99fc132 100644 --- a/src/generics/inference.md +++ b/src/generics/inference.md @@ -21,7 +21,7 @@ void main() { String s = boxOfString.data; - System.out.println(s); + IO.println(s); } ``` @@ -40,6 +40,6 @@ void main() { String s = boxOfString.data; - System.out.println(s); + IO.println(s); } ``` \ No newline at end of file diff --git a/src/generics/instantiation.md b/src/generics/instantiation.md index ebdcf55..485f422 100644 --- a/src/generics/instantiation.md +++ b/src/generics/instantiation.md @@ -18,6 +18,6 @@ void main() { String s = boxOfString.data; - System.out.println(s); + IO.println(s); } ``` \ No newline at end of file diff --git a/src/generics/raw_types.md b/src/generics/raw_types.md index 3ae11d1..6dc80f8 100644 --- a/src/generics/raw_types.md +++ b/src/generics/raw_types.md @@ -32,7 +32,7 @@ void main() { b.data = "abc"; if (b.data instanceof String s) { - System.out.println(s); + IO.println(s); } } ``` diff --git a/src/getting_started.md b/src/getting_started.md index 64fc8cd..f2b4e68 100644 --- a/src/getting_started.md +++ b/src/getting_started.md @@ -11,7 +11,7 @@ edit the following code. ```java void main() { - System.out.println("Hello, World"); + IO.println("Hello, World"); } ``` diff --git a/src/global_fields.md b/src/global_fields.md index dbf4718..0da8b02 100644 --- a/src/global_fields.md +++ b/src/global_fields.md @@ -10,9 +10,9 @@ in the "world" of our program.[^wonthold] int number = 0; void main() { - System.out.println(number); + IO.println(number); number++; - System.out.println(number); + IO.println(number); } ``` diff --git a/src/global_fields/challenges.md b/src/global_fields/challenges.md new file mode 100644 index 0000000..9358534 --- /dev/null +++ b/src/global_fields/challenges.md @@ -0,0 +1 @@ +# Challenges diff --git a/src/global_fields/default_values.md b/src/global_fields/default_values.md index c4beb57..9c27322 100644 --- a/src/global_fields/default_values.md +++ b/src/global_fields/default_values.md @@ -8,7 +8,7 @@ int x; int y = 5; void main() { - System.out.println(x); - System.out.println(y); + IO.println(x); + IO.println(y); } ``` diff --git a/src/global_fields/field_access.md b/src/global_fields/field_access.md index 581ed17..76c054c 100644 --- a/src/global_fields/field_access.md +++ b/src/global_fields/field_access.md @@ -9,12 +9,12 @@ final String monster = "Dracula"; class Mash { void itWasThe() { - System.out.println(monster + " and his son"); + IO.println(monster + " and his son"); } } void main() { - System.out.println(monster + " was there"); + IO.println(monster + " was there"); Mash mash = new Mash(); mash.itWasThe(); diff --git a/src/global_fields/final_fields.md b/src/global_fields/final_fields.md index a7d398d..fe7bdce 100644 --- a/src/global_fields/final_fields.md +++ b/src/global_fields/final_fields.md @@ -7,7 +7,7 @@ global final fields. final int x = 0; void main() { - System.out.println(x); + IO.println(x); } ``` diff --git a/src/global_fields/inferred_types.md b/src/global_fields/inferred_types.md index 5f8a2f1..cc514d2 100644 --- a/src/global_fields/inferred_types.md +++ b/src/global_fields/inferred_types.md @@ -7,6 +7,6 @@ for inferring the type of a global field. You need to explicitly write out the t var x = 5; void main() { - System.out.println(x); + IO.println(x); } ``` \ No newline at end of file diff --git a/src/growable_arrays/performance_problems.md b/src/growable_arrays/performance_problems.md index bf3f5b1..c138de9 100644 --- a/src/growable_arrays/performance_problems.md +++ b/src/growable_arrays/performance_problems.md @@ -56,7 +56,7 @@ class Main { array.add(i); } - System.out.println(array.size()); + IO.println(array.size()); } } ``` diff --git a/src/growable_arrays/usage.md b/src/growable_arrays/usage.md index 812b05c..dcdea5b 100644 --- a/src/growable_arrays/usage.md +++ b/src/growable_arrays/usage.md @@ -56,7 +56,7 @@ class Main { // And we can loop over it like so for (int i = 0; i < array.size(); i++) { - System.out.println(array.get(i)); + IO.println(array.get(i)); } } } diff --git a/src/hash_maps.md b/src/hash_maps.md index dc781ef..ab36962 100644 --- a/src/hash_maps.md +++ b/src/hash_maps.md @@ -1,27 +1,36 @@ -# Hash Maps +# HashMap -If you want to find an element in an array you usually +Arrays and their growable cousin `ArrayList` store a sequence of elements. +This is often enough, but if you want to find an element in an array you usually need to check every element one by one. ```java -String[] carsCharacters = new String[] { - "Tow Mater", - "Lightning McQueen", - "Doc Hudson" -}; - -boolean wasInCars(String name) { - for (var characterName : carsCharacters) { - if (characterName.equals(name)) { - return true; +record Character( + String name, + boolean protaganist +) {} + +Character findCharacter( + Character[] cast, + String name +) { + for (var character : cast) { + if (character.name().equals(name)) { + return character; } } - return false; + return null; } void main() { - System.out.println(wasInCars("Lightning McQueen")); - System.out.println(wasInCars("Blade Ranger")); + Character[] carsCharacters = new Character[] { + new Character("Tow Mater", false), + new Character("Lightning McQueen", true), + new Character("Doc Hudson", false) + }; + + IO.println(findCharacter(carsCharacters, "Lightning McQueen")); + IO.println(findCharacter(carsCharacters, "Blade Ranger")); } ``` @@ -30,4 +39,35 @@ For small arrays, this is no biggie. A computer can check over a few dozen thing But for big arrays, this stinks. What we want is some way to quickly look up something, regardless of how many things there are to -check over. This is what a Hash Map gives us. \ No newline at end of file +check over. This is what a HashMap gives us. + +```java +import java.util.HashMap; + +record Character( + String name, + boolean protaganist +) {} + +void main() { + HashMap carsCharacters = new HashMap<>(); + + carsCharacters.put( + "Tow Mater", + new Character("Tow Mater", false) + ); + + carsCharacters.put( + "Lightning McQueen", + new Character("Lightning McQueen", false) + ); + + carsCharacters.put( + "Doc Hudson", + new Character("Doc Hudson", false) + ); + + IO.println(carsCharacters.get("Lightning McQueen")); + IO.println(carsCharacters.get("Blade Ranger")); +} +``` \ No newline at end of file diff --git a/src/hash_maps/appropriate_keys.md b/src/hash_maps/appropriate_keys.md new file mode 100644 index 0000000..482a80a --- /dev/null +++ b/src/hash_maps/appropriate_keys.md @@ -0,0 +1,74 @@ +# Appropriate Keys + +Both objects with reference based and value based definitions of `equals` and `hashCode` +are "appropriate" to use as keys in `HashMap`s. + +The most important thing to be careful of using objects where`equals` and `hashCode` +are value based, but the object itself is mutable. + +```java +import java.util.Objects; +import java.util.HashMap; +~ +~void main() { +~ new Main().main(); +~} +~ +class Pos { + int x; + int y; + + Pos(int x, int y) { + this.x = x; + this.y = y; + } + + // equals and hashCode here are defined in terms of X and Y + @Override + public int hashCode() { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object o) { + return o instanceof Pos p && + this.x == p.x && + this.y == p.y; + } +} + +class Main { + void main() { + var m = new HashMap(); + + // Therefore we can get and put values using a Pos + // as a key. + var p = new Pos(4, 5); + m.put(p, "Slippery Ice"); + + IO.println( + m.get(p) + ); + + // But if we were to mutate the object + p.x = 99; + + // then the original object might be in the wrong bucket + // inside the hash map! + IO.println( + m.get(p) + ); + + // And because the key is stored, even if it is in the right bucket + // the equals check might not function correctly + IO.println( + m.get(new Pos(4, 5)) + ); + } +} +``` + +So while `Pos` has a "value based identity," you can muck it up by directly changing values. `HashMap`s assume +that when something is used as a key its `equals` and `hashCode` are stable and will not change later in the program. + +If something has a value-based definition of `equals` and `hashCode` but can be changed, that is inappropriate to use as a key. \ No newline at end of file diff --git a/src/hash_maps/buckets.md b/src/hash_maps/buckets.md index 5353323..d3f26a8 100644 --- a/src/hash_maps/buckets.md +++ b/src/hash_maps/buckets.md @@ -60,16 +60,16 @@ void main() { // These two have different hashes but end up in the // same bucket var sally = new CarsCharacter("Sally", "Carrera"); - System.out.println(hashFunction(sally)); - System.out.println(indexFor(hashFunction(sally))); + IO.println(hashFunction(sally)); + IO.println(indexFor(hashFunction(sally))); var doc = new CarsCharacter("Doc", "Hudson"); - System.out.println(hashFunction(doc)); - System.out.println(indexFor(hashFunction(doc))); + IO.println(hashFunction(doc)); + IO.println(indexFor(hashFunction(doc))); var lightning = new CarsCharacter("Lightning", "McQueen"); - System.out.println(hashFunction(lightning)); - System.out.println(indexFor(hashFunction(lightning))); + IO.println(hashFunction(lightning)); + IO.println(indexFor(hashFunction(lightning))); } diff --git a/src/hash_maps/filing_cabinets.md b/src/hash_maps/filing_cabinets.md index 43f02c9..8c0be0b 100644 --- a/src/hash_maps/filing_cabinets.md +++ b/src/hash_maps/filing_cabinets.md @@ -2,7 +2,7 @@ The basic concept behind a Hash Map is the same concept as a filing cabinet. -If you grew up in a doctor's office like I did this will make sense. If you didn't, Google +If you grew up in a doctor's office like I did this will make sense. If you didn't, look up what a filing cabinet looks like. Basically, if you have folders for a bunch of people you can separate them into different cabinets. diff --git a/src/hash_maps/get_items.md b/src/hash_maps/get_items.md index 6a50c14..5d170c2 100644 --- a/src/hash_maps/get_items.md +++ b/src/hash_maps/get_items.md @@ -1,133 +1,19 @@ # Get Items -Similarly to get an item from a map -you need to do the following - -1. Compute the hash code of the item. -2. Find the bucket that item should go in. -3. Go through each item in the bucket to see if it matches.[^more] +You can get the value associated with a key by using `.get`. If there is no +value associated with that key it will return `null`. ```java -record CarsCharacter( - String firstName, - String lastName -) {} - -class Bucket { - private CarsCharacter[] data; - private int size; - - Bucket() { - this.data = new CarsCharacter[0]; - this.size = 0; - } - - CarsCharacter get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - return this.data[index]; - } - - void set(int index, CarsCharacter value) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - this.data[index] = value; - } - - int size() { - return this.size; - } - - void add(CarsCharacter value) { - if (size >= this.data.length - 1) { - int newLength = this.data.length * 2; - if (newLength == 0) { - newLength = 2; - } - - CarsCharacter[] newArray = new CarsCharacter[newLength]; - for (int i = 0; i < this.data.length; i++) { - newArray[i] = this.data[i]; - } - this.data = newArray; - } - - this.data[size] = value; - this.size++; - } -} - +import java.util.HashMap; - -class CarsCharacterHashMap { - Bucket[] buckets = new Bucket[] { - new Bucket(), - new Bucket(), - new Bucket() - }; - - int indexFor(char hash) { - if (hash >= 'A' && hash <= 'F') { - return 0; - } - else if (hash >= 'G' && hash <= 'N') { - return 1; - } - else { - return 2; - } - } - - char hashFunction( - CarsCharacter carsCharacter - ) { - return carsCharacter.lastName().charAt(0); - } - - void put(String lastName, CarsCharacter character) { - // 1. Compute the hash code - char hash = hashFunction(lastName); - // 2. Find the bucket - Bucket bucket = buckets[indexFor(hash)]; - // 3. Add to the bucket - bucket.add(character); - } -} - - CarsCharacter get(String lastName) { - // 1. Compute the hash code - char hash = hashFunction(character); - // 2. Find the bucket - Bucket bucket = buckets[indexFor(hash)]; - // 3. Go through everything in the bucket - for (int i = 0; i < bucket.size(); i++) { - CarsCharacter inBucket = bucket.get(i); - if (inBucket.equals(character)) { - return inBucket; // Return any matches - } - } - - // Null if no matches - return null; +class Main { + void main() { + var wins = new HashMap(); + wins.put("Lightning McQueen", 2); + wins.put("Tow Mater", 0); + + IO.println(wins.get("Tow Mater")); + IO.println(wins.get("Doc Hudson")); } } - -void main() { - var map = new CarsCharacterHashMap(); - - map.put("Carrera", new CarsCharacter("Sally", "Carrera")); - map.put("Hudson", new CarsCharacter("Doc", "Hudson")); - map.put("McQueen", new CarsCharacter("Lightning", "McQueen")); - - - System.out.println(map.buckets[0].size()); - System.out.println(map.buckets[1].size()); - System.out.println(map.buckets[2].size()); -} ``` - -[^more]: There is more to say on how we determine if an item is a match, but -we will cover that later. For now just assume calling `.equals` will -make sense. diff --git a/src/hash_maps/growable_buckets.md b/src/hash_maps/growable_buckets.md deleted file mode 100644 index 5d33e82..0000000 --- a/src/hash_maps/growable_buckets.md +++ /dev/null @@ -1,62 +0,0 @@ -# Growable Buckets - -As you may have reasoned, a hash map's buckets -will start out empty but later have elements added to them. - -This means that they will need to grow over time. - -One way to do this is with a growable array. - -```java -class Bucket { - private CarsCharacter[] data; - private int size; - - Bucket() { - this.data = new CarsCharacter[0]; - this.size = 0; - } - - CarsCharacter get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - return this.data[index]; - } - - void set(int index, CarsCharacter value) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - this.data[index] = value; - } - - int size() { - return this.size; - } - - void add(CarsCharacter value) { - if (size >= this.data.length - 1) { - int newLength = this.data.length * 2; - if (newLength == 0) { - newLength = 2; - } - - CarsCharacter[] newArray = new CarsCharacter[newLength]; - for (int i = 0; i < this.data.length; i++) { - newArray[i] = this.data[i]; - } - this.data = newArray; - } - - this.data[size] = value; - this.size++; - } -} -``` - -Every detail of this is the same as the `GrowableIntArray` we made earlier, -save the fact that it is called `Bucket` and instead of `int`s it holds -`CarsCharacter`s.[^makenote] - -[^makenote]: Make note of this annoying similarity somewhere in your mind. It will come up later. \ No newline at end of file diff --git a/src/hash_maps/hash_collision.md b/src/hash_maps/hash_collision.md index 001d98b..b1c22f1 100644 --- a/src/hash_maps/hash_collision.md +++ b/src/hash_maps/hash_collision.md @@ -11,4 +11,4 @@ We call this situation a "hash collision." All it means is that these two object will definitely go into the same bucket. That's allowed to happen, but if _everything_ goes into the same bucket then there -isn't much point to using a hash map over a growable array. \ No newline at end of file +isn't much point to using a `HashMap` over an `ArrayList`. \ No newline at end of file diff --git a/src/hash_maps/hash_distribution.md b/src/hash_maps/hash_distribution.md index fed0d14..61ba6ca 100644 --- a/src/hash_maps/hash_distribution.md +++ b/src/hash_maps/hash_distribution.md @@ -10,4 +10,7 @@ For that scenario, using the first letter of the last name is a non-ideal hash f because so when many people have last names starting with the same letter, they will not be evenly distributed amongst the buckets. +Making a hash function with a good distribution is hard. `Objects.hash` will do a decent job of it +and thats why we use it. + [^irish]: "Mc" and "Mac" are common irish surnames and Boston has a sizable irish population. \ No newline at end of file diff --git a/src/hash_maps/hash_functions.md b/src/hash_maps/hash_functions.md index 280e867..bb8a5ce 100644 --- a/src/hash_maps/hash_functions.md +++ b/src/hash_maps/hash_functions.md @@ -19,7 +19,7 @@ void main() { var lightning = new CarsCharacter("Lightning", "McQueen"); char firstOfLast = hashFunction(lightning.lastName()); - System.out.println(firstOfLast); + IO.println(firstOfLast); } ``` diff --git a/src/hash_maps/keys_and_values.md b/src/hash_maps/keys_and_values.md index 905120d..09af8bc 100644 --- a/src/hash_maps/keys_and_values.md +++ b/src/hash_maps/keys_and_values.md @@ -3,10 +3,4 @@ A Hash Map "maps" keys to values. In the filing cabinet analogy we are mapping names to charts -full of all sorts of other information on the person. - -For the rest of these examples assume we are trying to map -`CarsCharacter`s to how many times they won the Piston Cup.[^watchcars] - -[^watchcars]: Cars was a delightful movie. Go to your local second hand store, get -a copy on DVD, and make a night of it. \ No newline at end of file +full of all sorts of other information on the person. \ No newline at end of file diff --git a/src/hash_maps/put_items.md b/src/hash_maps/put_items.md index 3427184..7ac2442 100644 --- a/src/hash_maps/put_items.md +++ b/src/hash_maps/put_items.md @@ -1,113 +1,20 @@ # Put Items -Putting it all together[^getit], we can "put" -items into our hash map by following these steps. - -1. Compute the hash code of the key. -2. Find the bucket that item should go in based on the key. -3. Add the item to the bucket. +You can associate a key with a value by calling the `.put` method.[^watchcars] ```java -record CarsCharacter( - String firstName, - String lastName -) {} - -class Bucket { - private CarsCharacter[] data; - private int size; - - Bucket() { - this.data = new CarsCharacter[0]; - this.size = 0; - } - - CarsCharacter get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - return this.data[index]; - } - - void set(int index, CarsCharacter value) { - if (index >= size) { - throw new IndexOutOfBoundsException(index); - } - this.data[index] = value; - } +import java.util.HashMap; - int size() { - return this.size; - } - - void add(CarsCharacter value) { - if (size >= this.data.length - 1) { - int newLength = this.data.length * 2; - if (newLength == 0) { - newLength = 2; - } - - CarsCharacter[] newArray = new CarsCharacter[newLength]; - for (int i = 0; i < this.data.length; i++) { - newArray[i] = this.data[i]; - } - this.data = newArray; - } - - this.data[size] = value; - this.size++; +class Main { + void main() { + var wins = new HashMap(); + wins.put("Lightning McQueen", 2); + wins.put("Tow Mater", 0); + + IO.println(wins); } } - - - -class CarsCharacterHashMap { - Bucket[] buckets = new Bucket[] { - new Bucket(), - new Bucket(), - new Bucket() - }; - - int indexFor(char hash) { - if (hash >= 'A' && hash <= 'F') { - return 0; - } - else if (hash >= 'G' && hash <= 'N') { - return 1; - } - else { - return 2; - } - } - - char hashFunction( - String lastName - ) { - return lastName.charAt(0); - } - - void put(String lastName, CarsCharacter character) { - // 1. Compute the hash code - char hash = hashFunction(lastName); - // 2. Find the bucket - Bucket bucket = buckets[indexFor(hash)]; - // 3. Add to the bucket - bucket.add(character); - } -} - -void main() { - var map = new CarsCharacterHashMap(); - - map.put("Carrera", new CarsCharacter("Sally", "Carrera")); - map.put("Hudson", new CarsCharacter("Doc", "Hudson")); - map.put("McQueen", new CarsCharacter("Lightning", "McQueen")); - - System.out.println(map.buckets[0].size()); - System.out.println(map.buckets[1].size()); - System.out.println(map.buckets[2].size()); -} ``` - -[^getit]: ayy \ No newline at end of file +[^watchcars]: Cars was a delightful movie. Go to your local second hand store, get +a copy on DVD, and make a night of it. \ No newline at end of file diff --git a/src/hash_maps/reference_based_identity.md b/src/hash_maps/reference_based_identity.md new file mode 100644 index 0000000..46d4706 --- /dev/null +++ b/src/hash_maps/reference_based_identity.md @@ -0,0 +1,83 @@ +# Reference Based Identity + +The hash function that `HashMap` uses is `.hashCode()`. It uses the number +returned from this method to decide which of the buckets it maintains +to put an item into. + +Instead of making buckets like `A-G` and `H-Z`, it will use ranges of numbers. The concept is the +same though + +For classes you make yourself, their `hashCode` will be based on what we call an object's +"identity." This means that while different instances of a class might give the same + + +```java +~void main() { +~ new Main().main(); +~} +~ +class LivingRaceCar { + int cursedness; + + // God, could you imagine being judged by a Honda? + LivingRaceCar(int cursedness) { + this.cursedness = cursedness; + } +} + +class Main { + void main() { + var carA = new LivingRaceCar(10); + // Car B is a reference to Car A + var carB = carA; + // Car C is a distinct object with its own identity + var carC = new LivingRaceCar(10); + + // Accordingly, Car C will probably have a different + // hashCode. This is despite it having the same + // values in its fields. It is a "distinct" object. + IO.println("A: " + carA.hashCode()); + IO.println("B: " + carB.hashCode()); + IO.println("C: " + carC.hashCode()); + } +} +``` + +Identity is also what the default `.equals` implementation is based off of. +If two variables reference the same object then `.equals` will return `true`. + +```java +~void main() { +~ new Main().main(); +~} +~ +class LivingRaceCar { + int cursedness; + + LivingRaceCar(int cursedness) { + this.cursedness = cursedness; + } +} + +class Main { + void main() { + var carA = new LivingRaceCar(10); + // Car B is a reference to Car A + var carB = carA; + // Car C is a distinct object with its own identity + var carC = new LivingRaceCar(10); + + // Car C therefore only equal itself + // Car A and B will equal eachother + IO.println("A.equals(A): " + carA.equals(carA)); + IO.println("A.equals(B): " + carA.equals(carB)); + IO.println("A.equals(C): " + carA.equals(carC)); + IO.println("B.equals(A): " + carB.equals(carA)); + IO.println("B.equals(B): " + carB.equals(carB)); + IO.println("B.equals(C): " + carB.equals(carC)); + IO.println("C.equals(A): " + carC.equals(carA)); + IO.println("C.equals(B): " + carC.equals(carB)); + IO.println("C.equals(C): " + carC.equals(carC)); + } +} +``` \ No newline at end of file diff --git a/src/hash_maps/ubiquity.md b/src/hash_maps/ubiquity.md new file mode 100644 index 0000000..fd80ce0 --- /dev/null +++ b/src/hash_maps/ubiquity.md @@ -0,0 +1,9 @@ +# Ubiquity + +HashMaps are very common both in Java and the wider programming world +as a whole. + +You can mostly get away with just knowing how to use them and what they do, +but if you want to take a deeper dive this video is a good start. + + \ No newline at end of file diff --git a/src/hash_maps/value_based_identity.md b/src/hash_maps/value_based_identity.md new file mode 100644 index 0000000..21ff029 --- /dev/null +++ b/src/hash_maps/value_based_identity.md @@ -0,0 +1,57 @@ +# Value Based Identity + +While reference based identity can be useful, its often not what you want for keys in a `HashMap`. +Ideally if you are looking up `"Tow Mater"` you shouldn't have to be careful to ensure its the *same* +instance of `String`, all you care about is that it contains the right characters. + +We call this notion of identity "value based." Two things are the same if they contain the same data - i.e. if they represent the same value. + +```java +class Main { + void main() { + // Strings A and B are distinct instances + var stringA = new String(new char[] { 'a', 'b', 'c' }); + var stringB = new String(new char[] { 'a', 'b', 'c' }); + + // but they will give the same hashCode + IO.println(stringA.hashCode()); + IO.println(stringB.hashCode()); + + // and will be equal to eachother + IO.println(stringA.equals(stringB)); + IO.println(stringB.equals(stringA)); + } +} +``` + +`String`s, all the numeric types like `Integer` and `Double`, as well as `Boolean`s are defined +in this way.[^difference] So will any `record`s and `enum`s you make[^bydefault]. + +```java +~void main() { +~ new Main().main(); +~} +~ +record Pos(int x, int y) {} + +class Main { + void main() { + // Positions A and B are distinct instances but hold the same values + var posA = new Pos(5, 5); + var posB = new Pos(5, 5); + + // therefore they will give the same hashCode + IO.println(posA.hashCode()); + IO.println(posB.hashCode()); + + // and will be equal to eachother + IO.println(posA.equals(posB)); + IO.println(posB.equals(posA)); + } +} +``` + + +[^difference]: There is an important distinction here between things where `.equals` and `.hashCode` are simply defined in terms of value based equality and whether the objects themselves have identity. Operations like `==` operate on what we might call an "intrinsic identity." Problem for later. + +[^bydefault]: At least by default. \ No newline at end of file diff --git a/src/inner_classes/disambiguation.md b/src/inner_classes/disambiguation.md index eafb54b..e0f1a12 100644 --- a/src/inner_classes/disambiguation.md +++ b/src/inner_classes/disambiguation.md @@ -29,9 +29,9 @@ class Car { int speed = 5; void saySpeed() { - System.out.println(speed); // 5 - System.out.println(this.speed); // 5 - System.out.println(Car.this.speed); // 0 + IO.println(speed); // 5 + IO.println(this.speed); // 5 + IO.println(Car.this.speed); // 0 } } } @@ -47,9 +47,9 @@ class Car { ~ int speed = 5; ~ ~ void saySpeed() { -~ System.out.println(speed); // 5 -~ System.out.println(this.speed); // 5 -~ System.out.println(Car.this.speed); // 0 +~ IO.println(speed); // 5 +~ IO.println(this.speed); // 5 +~ IO.println(Car.this.speed); // 0 ~ } ~ } ~} diff --git a/src/inner_classes/instances.md b/src/inner_classes/instances.md index 8591984..025eb87 100644 --- a/src/inner_classes/instances.md +++ b/src/inner_classes/instances.md @@ -27,7 +27,7 @@ class Main { var car = new Car(); var speedometer = car.getSpeedometer(); - System.out.println(speedometer); + IO.println(speedometer); // But this will not work // var speedometer = new Car.Speedometer(); diff --git a/src/inner_classes/new_operator.md b/src/inner_classes/new_operator.md index 6755155..976e212 100644 --- a/src/inner_classes/new_operator.md +++ b/src/inner_classes/new_operator.md @@ -9,7 +9,7 @@ something like `new ClassName()`, you can make an instance of an inner class by using `.new` on a variable that holds an instance of the containing class. -Thats a confusing verbal description, but it kinda makes sense once you see it. +That's a confusing verbal description, but it kinda makes sense once you see it. ```java class Car { @@ -27,7 +27,7 @@ class Main { void main() { Car car = new Car(); Car.Speedometer speedometer = car.new Speedometer(); - System.out.println(speedometer); + IO.println(speedometer); } } ``` diff --git a/src/instance_methods.md b/src/instance_methods.md index 2cc4255..bd247cb 100644 --- a/src/instance_methods.md +++ b/src/instance_methods.md @@ -11,7 +11,7 @@ class Muppet { String name; void freakOut() { - System.out.println("**ANGRY KERMIT NOISES**") + IO.println("**ANGRY KERMIT NOISES**") } } ``` diff --git a/src/instance_methods/arguments.md b/src/instance_methods/arguments.md index b0f6f78..54817ff 100644 --- a/src/instance_methods/arguments.md +++ b/src/instance_methods/arguments.md @@ -9,13 +9,13 @@ class Muppet { void singLyric(int verse) { if (verse == 1) { - System.out.println("Why are there so many"); + IO.println("Why are there so many"); } else if (verse == 2) { - System.out.println("Songs about rainbows"); + IO.println("Songs about rainbows"); } else { - System.out.println("And what's on the other side?"); + IO.println("And what's on the other side?"); } } } diff --git a/src/instance_methods/challenges.md b/src/instance_methods/challenges.md index 7240761..45cc118 100644 --- a/src/instance_methods/challenges.md +++ b/src/instance_methods/challenges.md @@ -51,13 +51,13 @@ void main() { view.value = new String[] { "A", "B", "C" }; // 3 - System.out.println(view.length()); + IO.println(view.length()); // A - System.out.println(view.get(0)); + IO.println(view.get(0)); // C - System.out.println(view.get(2)); + IO.println(view.get(2)); } ``` @@ -90,22 +90,22 @@ void main() { // "Masako Nozawa" String gokuFullName = goku.fullName(); - System.out.println(gokuFullName); + IO.println(gokuFullName); // "Nozawa" goku.firstName = null; gokuFullName = goku.fullName(); - System.out.println(gokuFullName); + IO.println(gokuFullName); // "No Name" goku.lastName = null; gokuFullName = goku.fullName(); - System.out.println(gokuFullName); + IO.println(gokuFullName); // "Horikawa" VoiceActor vegeta = new VoiceActor(); vegeta.lastName = "Horikawa"; - System.out.println(vegeta.fullName()); + IO.println(vegeta.fullName()); } ``` @@ -133,7 +133,7 @@ void main() { *** */ char[] c = rectangle.toCharArray(); - System.out.println(c); + IO.println(c); } ``` @@ -178,10 +178,10 @@ void main() { var taco = new Taco(); taco.deluxe(); - System.out.println("Has Beef: " + taco.beef); - System.out.println("Has Sour Cream: " + taco.sourCream); - System.out.println("Has Cheese: " + taco.cheese); - System.out.println("Has Onion: " + taco.onion); + IO.println("Has Beef: " + taco.beef); + IO.println("Has Sour Cream: " + taco.sourCream); + IO.println("Has Cheese: " + taco.cheese); + IO.println("Has Onion: " + taco.onion); } ``` @@ -201,6 +201,6 @@ class Oscar { void main() { var oscar = new Oscar(); oscar.setGrouchy(true); - System.out.println(oscar.grouchy); + IO.println(oscar.grouchy); } ``` \ No newline at end of file diff --git a/src/instance_methods/clarity.md b/src/instance_methods/clarity.md index dd1085e..a0e9f41 100644 --- a/src/instance_methods/clarity.md +++ b/src/instance_methods/clarity.md @@ -11,10 +11,10 @@ class Elmo { int age; void sayHello() { - System.out.println("Hi, I'm Elmo"); - System.out.print("I am "); - System.out.print(this.age); - System.out.println(" years old."); + IO.println("Hi, I'm Elmo"); + IO.print("I am "); + IO.print(this.age); + IO.println(" years old."); } } diff --git a/src/instance_methods/derived_values.md b/src/instance_methods/derived_values.md index 8228ee0..7114316 100644 --- a/src/instance_methods/derived_values.md +++ b/src/instance_methods/derived_values.md @@ -16,8 +16,8 @@ void main() { Elmo elmo = new Elmo(); elmo.age = 3; - System.out.println("Elmo is " + elmo.age + " right now,"); - System.out.println("but next year Elmo will be " + elmo.nextAge()); + IO.println("Elmo is " + elmo.age + " right now,"); + IO.println("but next year Elmo will be " + elmo.nextAge()); } ``` diff --git a/src/instance_methods/disambiguation.md b/src/instance_methods/disambiguation.md index d768fa5..ab70db4 100644 --- a/src/instance_methods/disambiguation.md +++ b/src/instance_methods/disambiguation.md @@ -17,7 +17,7 @@ void main() { elmo.age = 3; // true - System.out.println(elmo.isOlderThan(2)); + IO.println(elmo.isOlderThan(2)); } ``` diff --git a/src/instance_methods/field_access.md b/src/instance_methods/field_access.md index b062662..7e6e25d 100644 --- a/src/instance_methods/field_access.md +++ b/src/instance_methods/field_access.md @@ -8,12 +8,12 @@ class Elmo { int age; void sayHello() { - System.out.println("Hi, I'm Elmo"); - System.out.print("I am "); + IO.println("Hi, I'm Elmo"); + IO.print("I am "); // You can use elmo's age by just writing "age" - System.out.print(age); - System.out.println(" years old."); + IO.print(age); + IO.println(" years old."); } } diff --git a/src/instance_methods/field_updates.md b/src/instance_methods/field_updates.md index 9f03007..2ef4c4f 100644 --- a/src/instance_methods/field_updates.md +++ b/src/instance_methods/field_updates.md @@ -8,10 +8,10 @@ class Elmo { int age; void sayHello() { - System.out.println("Hi, I'm Elmo"); - System.out.print("I am "); - System.out.print(age); - System.out.println(" years old."); + IO.println("Hi, I'm Elmo"); + IO.print("I am "); + IO.print(age); + IO.println(" years old."); } void haveBirthday() { diff --git a/src/instance_methods/invocation.md b/src/instance_methods/invocation.md index cd86fc3..5f0d9ff 100644 --- a/src/instance_methods/invocation.md +++ b/src/instance_methods/invocation.md @@ -7,7 +7,7 @@ You then write `.` followed by the name of the instance method and a list of arg ```java class Elmo { void talkAboutRocko() { - System.out.println("ROCKO'S NOT ALIVE!!") + IO.println("ROCKO'S NOT ALIVE!!") } } diff --git a/src/instance_methods/invoke_other_methods.md b/src/instance_methods/invoke_other_methods.md index d15d11c..e8b42de 100644 --- a/src/instance_methods/invoke_other_methods.md +++ b/src/instance_methods/invoke_other_methods.md @@ -8,15 +8,15 @@ class Elmo { int age; void sayHello() { - System.out.println("Hi, I'm Elmo"); - System.out.print("I am "); - System.out.print(age); - System.out.println(" years old."); + IO.println("Hi, I'm Elmo"); + IO.print("I am "); + IO.print(age); + IO.println(" years old."); } void startTheShow(String showName) { sayHello(); - System.out.println("Welcome to " + showName); + IO.println("Welcome to " + showName); } } diff --git a/src/instance_methods/this.md b/src/instance_methods/this.md index 5b56b55..c121fdf 100644 --- a/src/instance_methods/this.md +++ b/src/instance_methods/this.md @@ -11,15 +11,15 @@ class Elmo { int age; void sayHello() { - System.out.println("Hi, I'm Elmo"); - System.out.print("I am "); - System.out.print(this.age); - System.out.println(" years old."); + IO.println("Hi, I'm Elmo"); + IO.print("I am "); + IO.print(this.age); + IO.println(" years old."); } void startTheShow(String showName) { this.sayHello(); - System.out.println("Welcome to " + showName); + IO.println("Welcome to " + showName); } } diff --git a/src/integers/addition.md b/src/integers/addition.md index ca2bade..06c1d2d 100644 --- a/src/integers/addition.md +++ b/src/integers/addition.md @@ -10,9 +10,9 @@ int y = x + 1; // z will be 11 int z = x + y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -24,7 +24,7 @@ int x = 5; // y will be 1 int y = x + -4; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` diff --git a/src/integers/challenges.md b/src/integers/challenges.md index a4be09f..eb7246e 100644 --- a/src/integers/challenges.md +++ b/src/integers/challenges.md @@ -13,7 +13,7 @@ What will this program output when run? Write down your guess and then try runni void main() { int x = 5; int y = 8; - System.out.println(x + y); + IO.println(x + y); } ``` @@ -27,7 +27,7 @@ void main() { x--; x--; x = x + x; - System.out.println(x); + IO.println(x); } ``` @@ -45,13 +45,13 @@ void main() { int z = 98; boolean xIsEven = < CODE HERE >; - System.out.println(xIsEven); + IO.println(xIsEven); boolean yIsEven = < CODE HERE >; - System.out.println(yIsEven); + IO.println(yIsEven); boolean zIsEven = < CODE HERE >; - System.out.println(zIsEven); + IO.println(zIsEven); } ``` @@ -63,7 +63,7 @@ Write down your guess and then try running the program below to see. ```java,editable void main() { - System.out.println(5 / 0); + IO.println(5 / 0); } ``` @@ -75,7 +75,7 @@ What can you write in the spot marked that will make the program output 2? void main() { int x = 5; int y = ; - System.out.println(x + y); + IO.println(x + y); } ``` @@ -85,7 +85,7 @@ What is the output of this code.[^fbarticle] ```java,editable void main() { - System.out.println( + IO.println( 6 / 2 * (1 + 2) ); } diff --git a/src/integers/division.md b/src/integers/division.md index 87f8ce6..7a1a389 100644 --- a/src/integers/division.md +++ b/src/integers/division.md @@ -8,8 +8,8 @@ int x = 8; // y will be 4 int y = x / 2; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` @@ -24,7 +24,7 @@ int x = 5 / 2; // 13 / 3 is not 4.3333, but instead 4. int y = 13 / 3; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` diff --git a/src/integers/equality.md b/src/integers/equality.md index 134f2da..a2c726c 100644 --- a/src/integers/equality.md +++ b/src/integers/equality.md @@ -10,14 +10,14 @@ and produces a `boolean` as its result. // 1 is never equal to 2 // this will be false boolean universeBroken = 1 == 2; -System.out.println(universeBroken); +IO.println(universeBroken); int loneliestNumber = 1; int canBeAsBadAsOne = 2; // this will be true boolean bothLonely = loneliestNumber == (canBeAsBadAsOne - 1); -System.out.println(bothLonely); +IO.println(bothLonely); ~} ``` @@ -30,6 +30,6 @@ The opposite check, whether things are not equal, can be done with `!=`. // 1 is never equal to 2 // this will be true boolean universeOkay = 1 != 2; -System.out.println(universeOkay); +IO.println(universeOkay); ~} ``` diff --git a/src/integers/limits.md b/src/integers/limits.md index 425b755..8ce9556 100644 --- a/src/integers/limits.md +++ b/src/integers/limits.md @@ -28,7 +28,7 @@ int atLimit = 2147483647; // The value will "loop around" to -2^31 int beyondLimit = atLimit + 1; // This will output -2147483648 -System.out.println(beyondLimit); +IO.println(beyondLimit); ~} ``` diff --git a/src/integers/multiplication.md b/src/integers/multiplication.md index bd20c08..e59cefc 100644 --- a/src/integers/multiplication.md +++ b/src/integers/multiplication.md @@ -11,8 +11,8 @@ int y = x * 5; // z will be 1125 int z = x * y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` diff --git a/src/integers/operator_precedence.md b/src/integers/operator_precedence.md index 9e28d38..cfe5fef 100644 --- a/src/integers/operator_precedence.md +++ b/src/integers/operator_precedence.md @@ -28,7 +28,7 @@ None of this should be a surprise if you learned [PEMDAS](https://www.khanacadem // and the final result is 17; int result = 2 * 3 + 3 * 9 / 2 - 2; -System.out.println(result); +IO.println(result); ~} ``` @@ -39,7 +39,7 @@ put them in the middle of any two math expressions. ~void main() { // The == check happens last. boolean areThingsSame = 3 * (4 - 1 + 3) * 4 == 5 * 3 + 1 * 3 * 9; -System.out.println(areThingsSame); +IO.println(areThingsSame); ~} ``` diff --git a/src/integers/reassignment.md b/src/integers/reassignment.md index b00359a..3d3708a 100644 --- a/src/integers/reassignment.md +++ b/src/integers/reassignment.md @@ -8,17 +8,17 @@ This is true for all data types, but it is easiest to demonstrate with numbers. ```java ~void main() { int x = 1; -System.out.println(x); +IO.println(x); // x starts as 1, 1 + 1 is 2. // 2 is the new value of x. x = x + 1; -System.out.println(x); +IO.println(x); // x is now 2, 2 * 2 * 3 is 12 // 12 is the new value of x. x = x * x * 3; -System.out.println(x); +IO.println(x); ~} ``` diff --git a/src/integers/remainder.md b/src/integers/remainder.md index 3d422d5..c03e872 100644 --- a/src/integers/remainder.md +++ b/src/integers/remainder.md @@ -13,9 +13,9 @@ int y = x % 2; // z will be 2 int z = x % 3; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -26,18 +26,18 @@ For instance, say you wanted to count from 0 up to 3 and then go back to 0. ```java ~void main() { int value = 0; -System.out.println(value); +IO.println(value); // the remainder of (0 + 1) divided by 3 is 1 // value will be 1 value = (value + 1) % 3; -System.out.println(value); +IO.println(value); // the remainder of (1 + 1) divided by 3 is 2 // value will be 2 value = (value + 1) % 3; -System.out.println(value); +IO.println(value); // the remainder of (2 + 1) divided by 3 is 0 @@ -46,12 +46,12 @@ System.out.println(value); // We never reach 3 because 3 divided by 3 // always has a remainder of zero. value = (value + 1) % 3; -System.out.println(value); +IO.println(value); // the remainder of (0 + 1) divided by 3 is 1 // value will be 1 value = (value + 1) % 3; -System.out.println(value); +IO.println(value); // and so on. // diff --git a/src/integers/shorthands_for_reassignment.md b/src/integers/shorthands_for_reassignment.md index 112a402..f9229fa 100644 --- a/src/integers/shorthands_for_reassignment.md +++ b/src/integers/shorthands_for_reassignment.md @@ -6,10 +6,10 @@ computed value back into the variable. ```java ~void main() { int x = 2; -System.out.println(x); +IO.println(x); x = x * 5; // 10 -System.out.println(x); +IO.println(x); ~} ``` @@ -40,7 +40,7 @@ x /= 6; x %= 3; // Pop quiz! -System.out.println(x); +IO.println(x); ~} ``` @@ -50,18 +50,18 @@ has its own special shorthand. ```java ~void main() { int x = 0; -System.out.println(x); +IO.println(x); // Same as // x = x + 1; // x += 1; x++; -System.out.println(x); +IO.println(x); // Same as // x = x - 1; // x -= 1; x--; -System.out.println(x); +IO.println(x); ~} ``` diff --git a/src/integers/subtraction.md b/src/integers/subtraction.md index 109a097..1342a66 100644 --- a/src/integers/subtraction.md +++ b/src/integers/subtraction.md @@ -10,9 +10,9 @@ int y = x - 1; // z will be 1 int z = x - y; -System.out.println(x); -System.out.println(y); -System.out.println(z); +IO.println(x); +IO.println(y); +IO.println(z); ~} ``` @@ -24,7 +24,7 @@ int x = 5; // y will be 9 int y = x - -4; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` diff --git a/src/integers_ii/base_16_integer_literals.md b/src/integers_ii/base_16_integer_literals.md index e00e500..fdd4232 100644 --- a/src/integers_ii/base_16_integer_literals.md +++ b/src/integers_ii/base_16_integer_literals.md @@ -19,10 +19,10 @@ To write an integer literal in Java which contains a hexadecimal number you writ ```java ~void main() { int sixteen = 0x10; -System.out.println(sixteen); +IO.println(sixteen); int twoHundredFiftyFive = 0xFF; -System.out.println(twoHundredFiftyFive); +IO.println(twoHundredFiftyFive); ~} ``` diff --git a/src/integers_ii/integer_from_a_base_16_string.md b/src/integers_ii/integer_from_a_base_16_string.md index d401347..08982af 100644 --- a/src/integers_ii/integer_from_a_base_16_string.md +++ b/src/integers_ii/integer_from_a_base_16_string.md @@ -9,7 +9,7 @@ String text = "C"; int twelve = Integer.parseInt(text, 16); -System.out.println(twelve); +IO.println(twelve); ~} ``` @@ -25,8 +25,8 @@ If you want to handle both hexadecimal numbers and regular base 10 numbers you s ```java ~void main() { -System.out.println(Integer.decode("0xC")); -System.out.println(Integer.decode("0x19")); -System.out.println(Integer.decode("19")); +IO.println(Integer.decode("0xC")); +IO.println(Integer.decode("0x19")); +IO.println(Integer.decode("19")); ~} ``` \ No newline at end of file diff --git a/src/integers_ii/integer_from_a_string.md b/src/integers_ii/integer_from_a_string.md index 0920866..39b4028 100644 --- a/src/integers_ii/integer_from_a_string.md +++ b/src/integers_ii/integer_from_a_string.md @@ -10,7 +10,7 @@ String text = "123"; int oneTwoThree = Integer.parseInt(text); -System.out.println(oneTwoThree); +IO.println(oneTwoThree); ~} ``` diff --git a/src/integers_ii/integer_to_a_base_16_string.md b/src/integers_ii/integer_to_a_base_16_string.md index fe2f3f1..36e1e40 100644 --- a/src/integers_ii/integer_to_a_base_16_string.md +++ b/src/integers_ii/integer_to_a_base_16_string.md @@ -9,6 +9,6 @@ int x = 29411; String xStr = Integer.toHexString(x); // 72e3 -System.out.println(xStr); +IO.println(xStr); ~} ``` \ No newline at end of file diff --git a/src/integers_ii/integer_to_a_string.md b/src/integers_ii/integer_to_a_string.md index c381362..15a5f4d 100644 --- a/src/integers_ii/integer_to_a_string.md +++ b/src/integers_ii/integer_to_a_string.md @@ -9,7 +9,7 @@ One is to "add it" to an empty string. int x = 4; String xStr = "" + x; -System.out.println(xStr); +IO.println(xStr); ~} ``` @@ -21,7 +21,7 @@ The other is to use the `toString` static method on `Integer`. int x = 4; String xStr = Integer.toString(x); -System.out.println(xStr); +IO.println(xStr); ~} ``` diff --git a/src/integers_ii/underscores_in_integer_literals.md b/src/integers_ii/underscores_in_integer_literals.md index f661cd7..e2189e6 100644 --- a/src/integers_ii/underscores_in_integer_literals.md +++ b/src/integers_ii/underscores_in_integer_literals.md @@ -11,8 +11,8 @@ between digits in an integer literal. int x = 1_000_000_000; int y = 10_000_000_000; -System.out.println(x); -System.out.println(y); +IO.println(x); +IO.println(y); ~} ``` @@ -22,6 +22,6 @@ This works with hexadecimal integer literals as well. ~void main() { int white = 0xFF_FF_FF; -System.out.println(Integer.toHexString(white)); +IO.println(Integer.toHexString(white)); ~} ``` \ No newline at end of file diff --git a/src/interfaces/implementation.md b/src/interfaces/implementation.md index 6ce34f6..5c51533 100644 --- a/src/interfaces/implementation.md +++ b/src/interfaces/implementation.md @@ -31,7 +31,7 @@ interface Dog { class Mutt implements Dog { public void bark() { - System.out.println("Bark"); + IO.println("Bark"); } public String fetch(String ball) { diff --git a/src/interfaces/multiple_implementations.md b/src/interfaces/multiple_implementations.md index da84c1d..87f2f2f 100644 --- a/src/interfaces/multiple_implementations.md +++ b/src/interfaces/multiple_implementations.md @@ -19,7 +19,7 @@ interface Dog { class Mutt implements Dog { @Override public void bark() { - System.out.println("Bark"); + IO.println("Bark"); } @Override @@ -31,7 +31,7 @@ class Mutt implements Dog { class Cat implements Dog { @Override public void bark() { - System.out.println("Meow"); + IO.println("Meow"); } @Override @@ -42,7 +42,7 @@ class Cat implements Dog { void barkAndFetch(Dog dog) { dog.bark(); - System.out.println(dog.fetch("Ball")); + IO.println(dog.fetch("Ball")); } void main() { diff --git a/src/interfaces/override.md b/src/interfaces/override.md index 765ed8a..1c18020 100644 --- a/src/interfaces/override.md +++ b/src/interfaces/override.md @@ -13,7 +13,7 @@ interface Dog { class Mutt implements Dog { @Override public void bark() { - System.out.println("Bark"); + IO.println("Bark"); } @Override diff --git a/src/interfaces/subtypes.md b/src/interfaces/subtypes.md index cd35a6d..b620718 100644 --- a/src/interfaces/subtypes.md +++ b/src/interfaces/subtypes.md @@ -16,7 +16,7 @@ interface Dog { class Mutt implements Dog { @Override public void bark() { - System.out.println("Bark"); + IO.println("Bark"); } @Override @@ -43,7 +43,7 @@ interface Dog { class Mutt implements Dog { @Override public void bark() { - System.out.println("Bark"); + IO.println("Bark"); } @Override @@ -57,7 +57,7 @@ void main() { dog.bark(); - System.out.println(dog.fetch("Ball")); + IO.println(dog.fetch("Ball")); } ``` diff --git a/src/interfaces_ii.md b/src/interfaces_ii.md new file mode 100644 index 0000000..729961b --- /dev/null +++ b/src/interfaces_ii.md @@ -0,0 +1,7 @@ +# Interfaces II + +Interfaces let you describe a common set of methods shared +by different implementing classes. + +They can do slightly more than this though and its helpful +to know about. \ No newline at end of file diff --git a/src/interfaces_ii/default_methods.md b/src/interfaces_ii/default_methods.md new file mode 100644 index 0000000..fc5b248 --- /dev/null +++ b/src/interfaces_ii/default_methods.md @@ -0,0 +1,78 @@ +# Default Methods + +Interfaces can specify a `default` implementation +for a method. + +```java,no_run +interface Dog { + void bark(); + + default void barkLoudly() { + IO.print("(loudly) "); + bark(); + } +} +``` + +Classes which implement interfaces do not need an explicit implementation +for methods which have a default.[^all] + +```java,no_run +interface Dog { + void bark(); + + default void barkLoudly() { + IO.print("(loudly) "); + bark(); + } +} + +class Poodle implements Dog { + @Override + public void bark() { + IO.println("bark!") + } +} +``` + +If the default implementation is not what you want, then that implementation +can be overrided. + +```java,no_run +interface Dog { + void bark(); + + default void barkLoudly() { + IO.print("(loudly) "); + bark(); + } +} + +class Poodle implements Dog { + @Override + public void bark() { + IO.println("bark!") + } + + @Override + public void barkLoudly() { + IO.println("BARK!") + } +} +``` + +If all of the methods on an interface have a default then you don't need to provide an implementation for any of them.[^allmethods] + +```java +interface Cat { + default void meow() { + IO.println("meow"); + } +} + +class Tabby implements Cat { + // Nothing needed to implement Cat +} +``` + +[^allmethods]: This is rarely useful, but not never. \ No newline at end of file diff --git a/src/interfaces_ii/interface_extension.md b/src/interfaces_ii/interface_extension.md new file mode 100644 index 0000000..d435c6b --- /dev/null +++ b/src/interfaces_ii/interface_extension.md @@ -0,0 +1,70 @@ +# Interface Extension + +Interfaces can extend other interfaces. + +```java,no_run +interface Dog { + void bark(); +} + +record Color(int r, int g, int b) {} + +interface ColoredDog extends Dog { + Color color(); +} +``` + +This means a few things. First an implementing class +must specify all the methods from both. + +```java,no_run +class Clifford implements ColoredDog { + @Override + public void bark() { // Must define the methods on Dog + IO.println("BARK BARK"); + } + + @Override + public Color color() { // As well as on ColoredDog + return new Color(255, 0, 0); // Red + } +} +``` + +Second, interface extension "establishes a subtyping relationship." +Something which implements a sub-interface can be used in a +place expecting the super-interface. + +```java,no_run +void main() { + Clifford clifford = new Clifford(); + clifford.bark(); + IO.println(clifford.color()); + + IO.println("-------"); + // clifford is a "ColoredDog" + ColoredDog coloredDog = clifford; + // So all the methods on ColoredDog are available + coloredDog.bark(); + IO.println(coloredDog.color()); + + + IO.println("-------"); + // all "ColoredDog"s are also "Dog"s + Dog dog = coloredDog; + // So you can use the methods from Dog, but not any + // from ColoredDog + dog.bark(); + // IO.println(dog.color()); - Won't work + + IO.println("-------"); + // and all "Dog"s are "Object"s + Object o = dog; + // So you only have access to the methods from Object unless you + // use instanceof to recover the actual type of the object + if (o instanceof ColoredDog c) { + c.bark(); + IO.println(c.color()); + } +} +``` diff --git a/src/interfaces_ii/static_fields.md b/src/interfaces_ii/static_fields.md new file mode 100644 index 0000000..996b9af --- /dev/null +++ b/src/interfaces_ii/static_fields.md @@ -0,0 +1,18 @@ +# Static Fields + +If there are constants associated with or useful for an interface, +those can be attached to the interface declaration as `public static final` fields. + +The declaration of these looks the same as declaring a normal field in a class. +The `public static final` are implied. + +```java +interface Biped { + // This does the same as + // "public static final int NUMBER_OF_FEET = 2" + // in a normal class. + int NUMBER_OF_FEET = 2; + + void walk(); +} +``` \ No newline at end of file diff --git a/src/interfaces_ii/static_methods.md b/src/interfaces_ii/static_methods.md new file mode 100644 index 0000000..7aa9699 --- /dev/null +++ b/src/interfaces_ii/static_methods.md @@ -0,0 +1,67 @@ +# Static Methods + +Interfaces may specify `static` methods. These work similarly to static methods +on classes in that they can be used without an actual instance of a class. + +```java +interface Animal { + static boolean allowedLooseInHouse(String species) { + if ("dog".equals(species) || "cat".equals(species)) { + return true; + } + else { + // My cousin has a Pig that we were all afraid + // was going to straight up eat her child. + // + // The child got old enough that its not a concern, + // but good god. + return false; + } + } +} + +void main() { + IO.println(Animal.allowedLooseInHouse("dog")); + IO.println(Animal.allowedLooseInHouse("cat")); + IO.println(Animal.allowedLooseInHouse("pig")); +} +``` + +You may use this mechanic for any reason, but often it is most convenient +for "factory methods" - methods which make it easy to construct objects +related to the interface hierarchy. + +A prime example of this is `List.of`. `of` is a static method defined on the `List` +interface. + +```java +void main() { + // of will return a List, but code using this + // factory doesn't see the actual implementing class + List critters = List.of("dog", "cat", "bat"); + IO.println(critters); +} +``` + +If the logic in a static interface method gets complex enough, it is also allowed +to define a private static method. + +This is unique for interfaces, as usually +everything in them is considered `public`. +Even without writing `public`, a `static` interface method is by default a public method. + +```java,no_run +interface Animal { + private static void eat() { + } + + private static void sleep() { + } + + static void live() { + eat(); + sleep(); + } +} +``` + diff --git a/src/iterable_and_iterator.md b/src/iterable_and_iterator.md deleted file mode 100644 index 5ca10f5..0000000 --- a/src/iterable_and_iterator.md +++ /dev/null @@ -1 +0,0 @@ -# Iterable and Iterator diff --git a/src/lambdas.md b/src/lambdas.md new file mode 100644 index 0000000..fa354f9 --- /dev/null +++ b/src/lambdas.md @@ -0,0 +1,2 @@ +# Lambdas + diff --git a/src/lambdas/functional_interface_annotation.md b/src/lambdas/functional_interface_annotation.md new file mode 100644 index 0000000..819e64b --- /dev/null +++ b/src/lambdas/functional_interface_annotation.md @@ -0,0 +1,25 @@ +# @FunctionalInterface + +If an interface is marked with the `@FunctionalInterface` annotation, +Java will verify that it fulfils the requirements for a functional interface. + +```java +@FunctionalInterface +interface BankRunner { + void runOnBank(); +} +~void main() {} +``` + +This is similar to the `@Override` annotation in that it doesn't affect how code works +but just adds in an extra guard rail. + +```java,does_not_compile +@FunctionalInterface +interface BankRunner { + // More than one required method, will error + void runOnBank(); + int applyInflation(int money); +} +~void main() {} +``` \ No newline at end of file diff --git a/src/lambdas/functional_interfaces.md b/src/lambdas/functional_interfaces.md new file mode 100644 index 0000000..0b109c7 --- /dev/null +++ b/src/lambdas/functional_interfaces.md @@ -0,0 +1,35 @@ +# Functional Interfaces + +If an interface has only a one method that needs to be implemented we would call that a "functional interface."[^SAM] + +```java +interface Runner { + // Only one method to implement + // "single abstract method" + void run(); +} +``` + + +Any other methods on the interface must be `default` or `static`. + +```java +interface Runner { + void run(); + + // Neither the default or static method are considered + default void runTwice() { + this.run(); + this.run(); + } + + static double milesToKilometers(double distance) { + return distance * 1.609; + } +} +``` + +Functions take input and return an output. We call them functional interfaces because with you can treat them as being functions whose input and output are the same as that one method to be implemented. + + +[^SAM]: You might also see these referred to as SAM interfaces. SAM for Single Abstract Method. \ No newline at end of file diff --git a/src/lambdas/lambda_expressions.md b/src/lambdas/lambda_expressions.md new file mode 100644 index 0000000..b2f3cf3 --- /dev/null +++ b/src/lambdas/lambda_expressions.md @@ -0,0 +1 @@ +# Lambda Expressions diff --git a/src/lambdas/method_references.md b/src/lambdas/method_references.md new file mode 100644 index 0000000..987e7b4 --- /dev/null +++ b/src/lambdas/method_references.md @@ -0,0 +1 @@ +# Method References diff --git a/src/loops/break.md b/src/loops/break.md index fbb9c03..bdb48a5 100644 --- a/src/loops/break.md +++ b/src/loops/break.md @@ -15,7 +15,7 @@ while (x > 0) { x--; } -System.out.println( +IO.println( "Final value of x is " + x ); ~} @@ -30,7 +30,7 @@ from an otherwise endless loop. ```java ~void main() { while (true) { - System.out.println( + IO.println( "The people started singing it not knowing what it was" ); diff --git a/src/loops/challenges.md b/src/loops/challenges.md index d8ff896..f70f187 100644 --- a/src/loops/challenges.md +++ b/src/loops/challenges.md @@ -35,7 +35,7 @@ What will this program output when run? Write down your guess and then try runni void main() { int x = 0; while (x < 10) { - System.out.println(x); + IO.println(x); x++; } } @@ -50,7 +50,7 @@ What will this program output when run? Write down your guess and then try runni void main() { int x = 0; while (x <= 10) { - System.out.println(x); + IO.println(x); x++; } } @@ -67,7 +67,7 @@ void main() { if (x % 3 == 0) { break; } - System.out.println(x); + IO.println(x); x++; } } @@ -85,7 +85,7 @@ void main() { if (x % 3 == 0) { continue; } - System.out.println(x); + IO.println(x); x++; } } @@ -102,7 +102,7 @@ void main() { while (x < 10) { int y = 2; while (y < 5) { - System.out.println(x * y); + IO.println(x * y); y++; } @@ -277,8 +277,8 @@ void main() { boolean shouldBreak = false; while (!shouldBreak && x < 100) { while (y < 100) { - System.out.println("x is " + x); - System.out.println("y is " + y); + IO.println("x is " + x); + IO.println("y is " + y); x = x * y; if (x == 0) { shouldBreak = true; @@ -288,7 +288,7 @@ void main() { } } - System.out.println("Done"); + IO.println("Done"); } ``` diff --git a/src/loops/continue.md b/src/loops/continue.md index fc4f700..aeac14f 100644 --- a/src/loops/continue.md +++ b/src/loops/continue.md @@ -12,7 +12,7 @@ while (x > 0) { if (x == 4) { continue; } - System.out.println(x + " is a good number"); + IO.println(x + " is a good number"); x--; } ~} diff --git a/src/loops/counting_down.md b/src/loops/counting_down.md index 4110733..0a58458 100644 --- a/src/loops/counting_down.md +++ b/src/loops/counting_down.md @@ -11,7 +11,7 @@ to stop at, and a line at the bottom of the loop which decrements the current nu ~void main() { int currentNumber = 100; while (currentNumber >= 1) { - System.out.println(currentNumber); + IO.println(currentNumber); currentNumber--; } ~} @@ -25,7 +25,7 @@ Similar to when counting up if the condition was not `currentNumber >= 1` and in int currentNumber = 100; // Stops at 2 while (currentNumber > 1) { - System.out.println(currentNumber); + IO.println(currentNumber); currentNumber--; } ~} diff --git a/src/loops/counting_up.md b/src/loops/counting_up.md index 030fc74..e29db0b 100644 --- a/src/loops/counting_up.md +++ b/src/loops/counting_up.md @@ -10,7 +10,7 @@ to stop at, and a line at the bottom of the loop which increments the current nu ~void main() { int currentNumber = 1; while (currentNumber <= 100) { - System.out.println(currentNumber); + IO.println(currentNumber); currentNumber++; } ~} @@ -25,7 +25,7 @@ it would stop at `99`. int currentNumber = 1; // Stops at 99 while (currentNumber < 100) { - System.out.println(currentNumber); + IO.println(currentNumber); currentNumber++; } ~} diff --git a/src/loops/do_while.md b/src/loops/do_while.md index 711b119..39fa1d0 100644 --- a/src/loops/do_while.md +++ b/src/loops/do_while.md @@ -6,7 +6,7 @@ One variation on a `while` loop is a "do-while loop." ~void main() { int x = 0; do { - System.out.println(x); + IO.println(x); x++; } while(x < 5); ~} @@ -28,11 +28,11 @@ is that the first time the loop is reached the condition for the loop is not che ~void main() { int x = 0; do { - System.out.println("this will run"); + IO.println("this will run"); } while (x != 0); while (x != 0) { - System.out.println("this will not run"); + IO.println("this will not run"); } ~} ``` diff --git a/src/loops/endless_loops.md b/src/loops/endless_loops.md index 94b7fad..75eb3f8 100644 --- a/src/loops/endless_loops.md +++ b/src/loops/endless_loops.md @@ -7,7 +7,7 @@ This can happen if the condition is a constant like `while (true)` ```java,no_run ~void main() { while (true) { - System.out.println("This is the song that never ends"); + IO.println("This is the song that never ends"); } ~} ``` @@ -19,7 +19,7 @@ Or if the variables tested in the condition are not updated inside of the loop. // x is never changed int x = 0; while (x != 1) { - System.out.println("It goes on and on my friends"); + IO.println("It goes on and on my friends"); } ~} ``` diff --git a/src/loops/iterate_over_a_string.md b/src/loops/iterate_over_a_string.md index 4aecc60..f80e840 100644 --- a/src/loops/iterate_over_a_string.md +++ b/src/loops/iterate_over_a_string.md @@ -10,7 +10,7 @@ String name = "Avril"; int index = 0; while (index < name.length()) { - System.out.println(name.charAt(index)); + IO.println(name.charAt(index)); index++; } ~} diff --git a/src/loops/iteration.md b/src/loops/iteration.md index 2f4ec38..6761ea0 100644 --- a/src/loops/iteration.md +++ b/src/loops/iteration.md @@ -11,7 +11,7 @@ while (x < 5) { // On the 2nd iteration x will be 1 // ... // On the final iteration x will be 4 - System.out.println(x); + IO.println(x); x++ } ~} diff --git a/src/loops/labeled_break.md b/src/loops/labeled_break.md index 8329ddb..8d769e4 100644 --- a/src/loops/labeled_break.md +++ b/src/loops/labeled_break.md @@ -44,10 +44,10 @@ while (x != 0) { break xLoop; } - System.out.println( + IO.println( "x is " + x ); - System.out.println( + IO.println( "y is " + y ); @@ -56,7 +56,7 @@ while (x != 0) { } } -System.out.println("done."); +IO.println("done."); ~} ``` diff --git a/src/loops/labeled_continue.md b/src/loops/labeled_continue.md index 6bffe93..cb36649 100644 --- a/src/loops/labeled_continue.md +++ b/src/loops/labeled_continue.md @@ -10,9 +10,9 @@ You just write `continue` followed by the label name. // Will keep going back to the top of the outer loop outerLoop: while (true) { - System.out.println("inside outer loop"); + IO.println("inside outer loop"); while (true) { - System.out.println("inside inner loop"); + IO.println("inside inner loop"); continue outerLoop; } } diff --git a/src/loops/nested_loops.md b/src/loops/nested_loops.md index 7059fc9..2de9c34 100644 --- a/src/loops/nested_loops.md +++ b/src/loops/nested_loops.md @@ -9,10 +9,10 @@ int y = 3; while (x != 0) { while (y != 0) { - System.out.println( + IO.println( "x is " + x ); - System.out.println( + IO.println( "y is " + y ); @@ -36,10 +36,10 @@ while (x != 0) { break; } - System.out.println( + IO.println( "x is " + x ); - System.out.println( + IO.println( "y is " + y ); @@ -62,10 +62,10 @@ while (x != 0) { while (y != 0) { - System.out.println( + IO.println( "x is " + x ); - System.out.println( + IO.println( "y is " + y ); diff --git a/src/loops/unreachable_code.md b/src/loops/unreachable_code.md index 64618ae..192afeb 100644 --- a/src/loops/unreachable_code.md +++ b/src/loops/unreachable_code.md @@ -11,7 +11,7 @@ Java knows this and so won't let any code like that run. while (true) { continue; - System.out.println("this is unreachable"); + IO.println("this is unreachable"); } ~} ``` diff --git a/src/loops/while.md b/src/loops/while.md index 181ed7d..364b00f 100644 --- a/src/loops/while.md +++ b/src/loops/while.md @@ -6,7 +6,7 @@ One way to make a loop in code is to use `while`. ~void main() { int x = 5; while (x != 0) { - System.out.println(x); + IO.println(x); x--; } ~} @@ -30,7 +30,7 @@ This will continue until the code in the condition evaluates to `false`. ~void main() { int glassesOfMilk = 99; while (glassesOfMilk > 0) { - System.out.println( + IO.println( glassesOfMilk + " glasses of milk left" ); diff --git a/src/loops_ii/break.md b/src/loops_ii/break.md index a5e3d9f..d86dfcb 100644 --- a/src/loops_ii/break.md +++ b/src/loops_ii/break.md @@ -9,9 +9,9 @@ for (int i = 0; i < 1000; i++) { if (i == 5) { break; } - System.out.println(i); + IO.println(i); } -System.out.println("Over"); +IO.println("Over"); // 0 // 1 diff --git a/src/loops_ii/comparison_to_while.md b/src/loops_ii/comparison_to_while.md index b8dfb11..a04655e 100644 --- a/src/loops_ii/comparison_to_while.md +++ b/src/loops_ii/comparison_to_while.md @@ -8,19 +8,19 @@ and the code needed with a `while` loop, there might not seem like much of a dif double[] numbers = { 4.4, 1.1, 4.1, 4.7 }; for (int index = 0; index < numbers.length; index++) { - System.out.println(numbers[index]); + IO.println(numbers[index]); } int index = 0; while (index < numbers.length) { - System.out.println(numbers[index]); + IO.println(numbers[index]); index++; } ~} ``` This is doubly true when we are looking at toy examples where the only thing done -with the element is `System.out.println`. +with the element is `IO.println`. The biggest benefit to a `for` is subtle. With a `while` based loop, the initializer and boolean expression can potentially be many lines from the statement which updates the variable. diff --git a/src/loops_ii/continue.md b/src/loops_ii/continue.md index e1e8dc8..d815603 100644 --- a/src/loops_ii/continue.md +++ b/src/loops_ii/continue.md @@ -12,7 +12,7 @@ for (int i = 0; i < 5; i++) { // i++ will still run continue; } - System.out.println(i); + IO.println(i); } // 0 @@ -31,7 +31,7 @@ while (i < 5) { if (i == 2) { continue; } - System.out.println(i); + IO.println(i); i++; } @@ -52,7 +52,7 @@ while (i < 5) { i++ continue; } - System.out.println(i); + IO.println(i); i++; } diff --git a/src/loops_ii/delayed_assignment.md b/src/loops_ii/delayed_assignment.md index 1da5f07..8272ac9 100644 --- a/src/loops_ii/delayed_assignment.md +++ b/src/loops_ii/delayed_assignment.md @@ -7,7 +7,7 @@ declared outside of the loop. ~void main() { int number; for (number = 0; number < 5; number++) { - System.out.println("At: " + number); + IO.println("At: " + number); } ~} ``` @@ -18,11 +18,11 @@ You might choose to do this so that after the loop is finished, you can still ac ~void main() { int number; for (number = 0; number < 5; number++) { - System.out.println("At: " + number); + IO.println("At: " + number); } // This will work, we can access the variable still. -System.out.println("Ended at: " + number); +IO.println("Ended at: " + number); ~} ``` @@ -32,10 +32,10 @@ to use that variable after the loop ```java ~void main() { for (int number = 0; number < 5; number++) { - System.out.println("At: " + number); + IO.println("At: " + number); } // This will not work. number is no longer available -System.out.println("Ended at: " + number); +IO.println("Ended at: " + number); ~} ``` diff --git a/src/loops_ii/drawing_isosceles_triangles.md b/src/loops_ii/drawing_isosceles_triangles.md index b17f8af..93109cf 100644 --- a/src/loops_ii/drawing_isosceles_triangles.md +++ b/src/loops_ii/drawing_isosceles_triangles.md @@ -16,7 +16,7 @@ and the second row needs one space character. ```java ~void main() { -System.out.println(" *\n ***\n*****"); +IO.println(" *\n ***\n*****"); ~} ``` @@ -27,12 +27,12 @@ So any loop we make needs to take this pattern into account. int totalRows = 5; for (int row = 1; row <= totalRows; row++) { for (int i = 0; i < totalRows - row; i++) { - System.out.print(" "); + IO.print(" "); } for (int i = 0; i < row * 2 - 1; i++) { - System.out.print("*"); + IO.print("*"); } - System.out.println(); + IO.println(); } ~} ``` diff --git a/src/loops_ii/drawing_right_triangles.md b/src/loops_ii/drawing_right_triangles.md index 392356e..2cc4c17 100644 --- a/src/loops_ii/drawing_right_triangles.md +++ b/src/loops_ii/drawing_right_triangles.md @@ -16,7 +16,7 @@ If you were to write the code out to print this explicitly it would look like th ```java ~void main() { -System.out.print("*\n**\n**\n"); +IO.print("*\n**\n**\n"); ~} ``` @@ -28,10 +28,10 @@ Since counting up `1 -> 2 -> 3` is easy with `for` loops, you can translate this ~void main() { for (int numberOfStars = 1; numberOfStars <= 3; numberOfStars++) { for (int i = 0; i < numberOfStars; i++) { - System.out.print("*"); + IO.print("*"); } - // Same as System.out.print("\n"); - System.out.println(); + // Same as IO.print("\n"); + IO.println(); } ~} ``` @@ -43,9 +43,9 @@ Which makes it easy to make one of these triangles however tall you want. int height = 6; for (int numberOfStars = 1; numberOfStars <= height; numberOfStars++) { for (int i = 0; i < numberOfStars; i++) { - System.out.print("*"); + IO.print("*"); } - System.out.println(); + IO.println(); } ~} ``` diff --git a/src/loops_ii/empty_expressions.md b/src/loops_ii/empty_expressions.md index f62ff5b..cb25568 100644 --- a/src/loops_ii/empty_expressions.md +++ b/src/loops_ii/empty_expressions.md @@ -5,7 +5,7 @@ You are also allowed to leave the expression part of a `for` loop blank. ```java ~void main() { for (int i = 0;;i++) { - System.out.println(i); + IO.println(i); } // 0 // 1 @@ -24,7 +24,7 @@ for (int i = 0;;i++) { if (i == 5) { break; } - System.out.println(i); + IO.println(i); } // 0 // 1 diff --git a/src/loops_ii/empty_initializers.md b/src/loops_ii/empty_initializers.md index ca4d59f..f6e39cc 100644 --- a/src/loops_ii/empty_initializers.md +++ b/src/loops_ii/empty_initializers.md @@ -7,7 +7,7 @@ so long as you still have the `;`. ~void main() { int number = 0; for (;number < 5; number++) { - System.out.println(number); + IO.println(number); } ~} ``` @@ -21,8 +21,8 @@ This way its initialization and declaration can be on the same line, which might ~void main() { int number = 0; for (;number < 5; number++) { - System.out.println(number); + IO.println(number); } -System.out.println("Still have number: " + number); +IO.println("Still have number: " + number); ~} ``` diff --git a/src/loops_ii/empty_statements.md b/src/loops_ii/empty_statements.md index cfc47ce..adac4a8 100644 --- a/src/loops_ii/empty_statements.md +++ b/src/loops_ii/empty_statements.md @@ -6,7 +6,7 @@ the end of an iteration there is nothing guaranteed to run. ```java ~void main() { for (int i = 6; i > 2;) { - System.out.println(i); + IO.println(i); i--; } @@ -23,14 +23,14 @@ If you leave both the initializer and statement blank, that will be functionally ~void main() { int number = 1; for (;number < 10;) { - System.out.println(number); + IO.println(number); number *= 2; } // Same logic as above int number2 = 1; while (number2 < 10) { - System.out.println(number2); + IO.println(number2); number2 *= 2; } ~} @@ -40,7 +40,7 @@ If you leave the initializer, expression, and statement blank it will be the sam ```java,no_run for (;;) { - System.out.println("The people stated singing it..."); + IO.println("The people stated singing it..."); } // Runs forever ``` diff --git a/src/loops_ii/final_variables.md b/src/loops_ii/final_variables.md index f150dda..68abcd4 100644 --- a/src/loops_ii/final_variables.md +++ b/src/loops_ii/final_variables.md @@ -6,7 +6,7 @@ The initializer of a `for` loop can also declare `final` variables. ~void main() { int i = 0; for (final String name = "Bob"; i < 5; i++) { - System.out.println(name + ": " + i); + IO.println(name + ": " + i); } ~} ``` @@ -20,9 +20,9 @@ can change without reassigning a variable. for (final char[] letters = { 'I', 'O', 'U' }; letters[0] != 'A';) { for (int i = 0; i < letters.length; i++) { letters[i] -= 1; - System.out.print(letters[i]); + IO.print(letters[i]); } - System.out.println(); + IO.println(); } // HNT diff --git a/src/loops_ii/for.md b/src/loops_ii/for.md index d51447d..ea86942 100644 --- a/src/loops_ii/for.md +++ b/src/loops_ii/for.md @@ -13,7 +13,7 @@ As with many things, this might be easiest to see by looking at an example. ~void main() { // Will run 10 times for (int number = 0; number < 10; number++) { - System.out.println(number); + IO.println(number); } ~} ``` @@ -24,7 +24,7 @@ That `for` loop works about the same as this `while` loop. ~void main() { int number = 0; while (number < 10) { - System.out.println(number); + IO.println(number); number++; } diff --git a/src/loops_ii/for_counting_up_and_down.md b/src/loops_ii/for_counting_up_and_down.md index e88c73f..93968a2 100644 --- a/src/loops_ii/for_counting_up_and_down.md +++ b/src/loops_ii/for_counting_up_and_down.md @@ -7,12 +7,12 @@ a given number. ~void main() { // Goes from 1 to 100 for (int currentNumber = 1; currentNumber <= 100; currentNumber++) { - System.out.println(currentNumber); + IO.println(currentNumber); } // Goes from 100 to 1 for (int currentNumber = 100; currentNumber >= 1; currentNumber--) { - System.out.println(currentNumber); + IO.println(currentNumber); } ~} ``` diff --git a/src/loops_ii/i.md b/src/loops_ii/i.md index cfce71d..db3f762 100644 --- a/src/loops_ii/i.md +++ b/src/loops_ii/i.md @@ -10,7 +10,7 @@ String word = "bird"; for (int i = 0; i < array.length; i++) { char letter = word.charAt(i); - System.out.println(letter); + IO.println(letter); } // b @@ -33,8 +33,8 @@ int[] numbers = { 1, 2 }; for (int i = 0; i < letters.length; i++) { for (int j = 0; j < numbers.length; j++) { - System.out.print(letters[i]); - System.out.println(numbers[j]); + IO.print(letters[i]); + IO.println(numbers[j]); } } diff --git a/src/loops_ii/inferred_types.md b/src/loops_ii/inferred_types.md index bdc7276..6c30882 100644 --- a/src/loops_ii/inferred_types.md +++ b/src/loops_ii/inferred_types.md @@ -6,7 +6,7 @@ you still are allowed to use `var` so that the type of the declared variable is ```java ~void main() { for (var i = 0; i < 10; i++) { - System.out.println(i); + IO.println(i); } ~} ``` @@ -19,7 +19,7 @@ But if your `for` loop is doing something more exotic, it might make sense. ```java ~void main() { for (var repeated = ""; repeated.length() < 5; repeated = repeated + "a") { - System.out.println(repeated); + IO.println(repeated); } // a diff --git a/src/loops_ii/iterate_over_a_string.md b/src/loops_ii/iterate_over_a_string.md index 2dd1a3a..9ac0273 100644 --- a/src/loops_ii/iterate_over_a_string.md +++ b/src/loops_ii/iterate_over_a_string.md @@ -8,7 +8,7 @@ you iterate over each character in a `String`. String name = "Lavigne"; for (int index = 0; index < name.length(); index++) { - System.out.println(name.charAt(index)); + IO.println(name.charAt(index)); } ~} ``` diff --git a/src/loops_ii/iterate_over_an_array.md b/src/loops_ii/iterate_over_an_array.md index b9b6dad..674b15c 100644 --- a/src/loops_ii/iterate_over_an_array.md +++ b/src/loops_ii/iterate_over_an_array.md @@ -8,7 +8,7 @@ you can use it to go through each element in an array. int[] numbers = { 4, 1, 6, 9 }; for (int index = 0; index < numbers.length; index++) { - System.out.println(numbers[index]); + IO.println(numbers[index]); } ~} ``` diff --git a/src/loops_ii/labeled_break.md b/src/loops_ii/labeled_break.md index 42026f1..23c9389 100644 --- a/src/loops_ii/labeled_break.md +++ b/src/loops_ii/labeled_break.md @@ -17,14 +17,14 @@ This applies also to when `while` loops are nested within `for` loops or the oth ~void main() { outerForLoop: for (int i = 0; i < 10; i++) { - System.out.println(i); + IO.println(i); while (i < 100) { if (i == 5) { break outerForLoop; } i++; } - System.out.println(i); + IO.println(i); } // 0 diff --git a/src/loops_ii/labeled_continue.md b/src/loops_ii/labeled_continue.md index f36b82d..850db54 100644 --- a/src/loops_ii/labeled_continue.md +++ b/src/loops_ii/labeled_continue.md @@ -8,7 +8,7 @@ the statement of a `for` loop will always run when you get to the top of it.[^un label: for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { - System.out.println ("" + i + ", " + j); + IO.println ("" + i + ", " + j); if (i == 2) { // i++ will run continue label; diff --git a/src/loops_iii.md b/src/loops_iii.md index 7c57168..0c573cc 100644 --- a/src/loops_iii.md +++ b/src/loops_iii.md @@ -14,7 +14,7 @@ String[] shirts = new String[] { for (int i = 0; i < shirts.length; i++) { String shirt = shirts[i]; - System.out.println(shirt); + IO.println(shirt); } ~} ``` @@ -30,7 +30,7 @@ String[] shirts = new String[] { }; for (String shirt : shirts) { - System.out.println(shirt); + IO.println(shirt); } ~} ``` diff --git a/src/loops_iii/arraylist.md b/src/loops_iii/arraylist.md new file mode 100644 index 0000000..271f2a9 --- /dev/null +++ b/src/loops_iii/arraylist.md @@ -0,0 +1,42 @@ +# ArrayList + +One class which implements `Iterable` is `ArrayList`. + +```java +~import java.util.ArrayList; +~ +~class Main { +~ void main() { +ArrayList donutEaters = new ArrayList<>(); +donutEaters.add("Chief Wiggum"); +donutEaters.add("Homer Simpson"); + +Iterator donutEatersIterator = donutEaters.iterator(); +// Check if there is a next element +while (donutEatersIterator.hasNext()) { + // If there is, get it and advance the iterator + String donutEater = donutEatersIterator.next(); + + IO.println(donutEater + " eats donuts"); +} +~ } +~} +``` + +This means you can loop over it with a for-each loop same as an array. + +```java +~import java.util.ArrayList; +~ +~class Main { +~ void main() { +ArrayList donutEaters = new ArrayList<>(); +donutEaters.add("Chief Wiggum"); +donutEaters.add("Homer Simpson"); + +for (String donutEater : donutEaters) { + IO.println(donutEater + " eats donuts"); +} +~ } +~} +``` \ No newline at end of file diff --git a/src/loops_iii/arrays.md b/src/loops_iii/arrays.md new file mode 100644 index 0000000..4ed25fe --- /dev/null +++ b/src/loops_iii/arrays.md @@ -0,0 +1,77 @@ +# Arrays + +When you use a for-each loop on an array, it acts the same as the kind of for-loop +you would have written before with an explicit index. + +```java +record Drink(String name, double mgCaffeinePerCup) {} + +~class Main { +~ void main() { +Drink[] drinks = { + new Drink("Black Coffee", 95), + new Drink("Milk", 0), + new Drink("Green Tea", 35) +}; + +// This loop functions the same as the loop below +for (int i = 0; i < drinks.length; i++) { + Drink drink = drinks[i]; + + IO.println( + drink.name() + + " has " + + drink.mgCaffeinePerCup() + + "mg caffiene per cup" + ); +} + +IO.println("------------------"); + +for (Drink drink : drinks) { + IO.println( + drink.name() + + " has " + + drink.mgCaffeinePerCup() + + "mg caffiene per cup" + ); +} +~ } +~} +``` + +This doesn't mean that the old style of for loop is useless now. You will still want that +if you need to know which element in your array you are dealing with +or if you are doing a more interesting form of iteration. + +```java +record Drink(String name, double mgCaffeinePerCup) {} + +~class Main { +~ void main() { +Drink[] drinks = { + new Drink("Black Coffee", 95), + new Drink("Milk", 0), + new Drink("Green Tea", 35) +}; + + +// If loop actually cares to use `i` beyond just accessing +// the right element in the array +for (int i = 0; i < drinks.length; i++) { + Drink drink = drinks[i]; + + // Which you might want for display logic + IO.println( + "[" + i + "]: " + drink.name() + ); + + // Or to mutate the original array + drinks[i] = new Drink( + drink.name(), + drink.mgCaffeinePerCup() + 100 + ); +} +~ } +~} +``` \ No newline at end of file diff --git a/src/loops_iii/concurrent_modifications.md b/src/loops_iii/concurrent_modifications.md index f6f973c..d9f99e5 100644 --- a/src/loops_iii/concurrent_modifications.md +++ b/src/loops_iii/concurrent_modifications.md @@ -1 +1,81 @@ # Concurrent Modifications + +If you are looping over a collection with a for-each loop you generally +cannot remove things from that collection at the same time. Doing so should +trigger a `ConcurrentModificationException`.[^should] + +```java,panics +~import java.util.ArrayList; +~ +record Sandwich( + int turkeySlices, + int cheeseSlices, + boolean mayo +) {} + +~class Main { +~ void main() { +ArrayList sandwiches = new ArrayList<>(); +var turkeyAndCheddar = new Sandwich(2, 2, true); +var grilledCheese = new Sandwich(0, 4, false); +var bigTurkeyAndCheddar = new Sandwich(10, 10, true); +var theWisconsinFreak = new Sandwich(0, 20, true); + +sandwiches.add(turkeyAndCheddar); +sandwiches.add(grilledCheese); +sandwiches.add(bigTurkeyAndCheddar); +sandwiches.add(theWisconsinFreak); + +for (Sandwich sandwich : sandwiches) { + if (sandwich.mayo()) { // Some people don't like Mayo + // But we can't get rid of them during the loop + sandwiches.remove(sandwich); + } +} +~ } +~} +``` + +If you want to remove an element at the same time as iterating over specifically an `ArrayList`, you +can get creative with indexes and regular loops.[^remove] + +```java +~import java.util.ArrayList; +~ +record Sandwich( + int turkeySlices, + int cheeseSlices, + boolean mayo +) {} + +~class Main { +~ void main() { +ArrayList sandwiches = new ArrayList<>(); +var turkeyAndCheddar = new Sandwich(2, 2, true); +var grilledCheese = new Sandwich(0, 4, false); +var bigTurkeyAndCheddar = new Sandwich(10, 10, true); +var theWisconsinFreak = new Sandwich(0, 20, true); + +sandwiches.add(turkeyAndCheddar); +sandwiches.add(grilledCheese); +sandwiches.add(bigTurkeyAndCheddar); +sandwiches.add(theWisconsinFreak); + +for (int i = 0; i < sandwiches.size(); i++) { + Sandwich sandwich = sandwiches.get(i); + if (sandwich.mayo()) { // Some people don't like Mayo + sandwiches.remove(sandwich); + i--; // Subtracting one from our current index syncs us back up + } +} + +IO.println(sandwiches); +~ } +~} +``` + +[^should]: The reason I say "should" is that doing the book keeping needed to know when +this has happened can be hard and not all `Iterator`s in the world will do it. The check is what we would call "best effort." + +[^remove]: And, as these footnotes have alluded, there is a `.remove` method on `Iterator`. We'll cover it +later. \ No newline at end of file diff --git a/src/loops_iii/for_each_loops.md b/src/loops_iii/for_each_loops.md index 7f73c77..d4fdc11 100644 --- a/src/loops_iii/for_each_loops.md +++ b/src/loops_iii/for_each_loops.md @@ -1 +1,38 @@ # For-each loops + +Where a normal `for` loop is more or less a shorthand for a certain kind of `while` loop, +a for-each loop[^enhanced] is a shorthand for the general concept of iterating over a collection +of elements. + +To use a for-each loop, write `for` then in the parentheses write a variable declaration, a `:`, and the +collection of elements you are iterating over. + +``` +for ( : ) { + +} +``` + +```java +record Bread(String name, boolean french) {} + +~class Main { +~ void main() { +Bread[] breads = { + new Bread("Croissant", true), + new Bread("Baguette", true), + new Bread("Boston Brown Bread", false) +}; + +for (Bread bread : breads) { + IO.println( + bread.name() + + (bread.french() ? " is french" : " is not french") + ); +} +~ } +~} +``` + + +[^enhanced]: You might see this referred to as an "enhanced for statement." [That is its name in the language spec](https://docs.oracle.com/javase/specs/jls/se23/html/jls-14.html#jls-14.14.2) but not the name most people will use. \ No newline at end of file diff --git a/src/loops_iii/inferred_types.md b/src/loops_iii/inferred_types.md new file mode 100644 index 0000000..8d10854 --- /dev/null +++ b/src/loops_iii/inferred_types.md @@ -0,0 +1,17 @@ +# Inferred Types + +A variable declaration in a for-each loop can make use of `var` to infer its type. + +```java +~class Main { +~ void main() { +String[] chairMaterials = { "wicker", "wood", "plastic" } +for (var chairMaterial : chairMaterials) { + IO.println(chairMaterial); +} +~ } +~} +``` + +And this is good to know for the same reasons you would use `var` other places in Java. +Writing `Book book` can be a drain on the soul. diff --git a/src/loops_iii/iterable_and_iterator.md b/src/loops_iii/iterable_and_iterator.md new file mode 100644 index 0000000..2f4d9e4 --- /dev/null +++ b/src/loops_iii/iterable_and_iterator.md @@ -0,0 +1,53 @@ +# Iterable and Iterator + +For things that are not arrays, a for-each loops +are built on top of two interfaces: `java.lang.Iterable` and `java.lang.Iterator`. + +The [`Iterator`](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/Iterator.html) interface defines two methods: `hasNext` and `next`[^remove]. Iterators let you box up the logic of +how to loop over something. + +```java,no_run +public interface Iterator { + // Will return true if there are more elements + // false otherwise + boolean hasNext(); + + // Gets an element and advances the iterator forwards + T next(); +} +``` + +[`Iterable`](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/Iterable.html) then +just has one method which gives you an `Iterator`. + +```java,no_run +interface Iterable { + // Gives a "fresh" Iterator + Iterator iterator(); +} +``` + +This is needed because `Iterator`s are "one shot." It starts at the beginning of a collection +and advances across every element each time `next` is called. In order to loop over something multiple +times you need a fresh iterator each time. + +A for-each loop over an `Iterable` object more or less translates to this style of `while` loop.[^important] + +```java,no_run +for (String thing : iterable) { + // ... +} + +// is the same as + +Iterator iter = iterable.iterator(); +while (iter.hasNext()) { + String thing = iter.next(); + // ... +} +``` + + +[^remove]: There is actually one more method: `remove`. Not all `Iterator`s support it so we'll cover it once we've introduced more `Iterable` things. + +[^important]: I think this is important to know because otherwise it won't make sense when you run in to things you can loop over but don't have `.get`/`[]`, ` \ No newline at end of file diff --git a/src/loops_iii/string.md b/src/loops_iii/string.md new file mode 100644 index 0000000..64b8bc4 --- /dev/null +++ b/src/loops_iii/string.md @@ -0,0 +1,28 @@ +# String + +One thing that feels like it _should_ implement the `Iterable` interface but does not is `String`. + +```java,does_not_compile +~class Main { +~ void main() { +String letters = "abc"; +for (char letter : letters) { + IO.println(letter); +} +~ } +~} +``` + +To loop over all the characters in a `String`, you have to use a regular loop. + +```java,does_not_compile +~class Main { +~ void main() { +String letters = "abc"; +for (int i = 0; i < letters.length(); i++) { + char letter = letters.charAt(i); + IO.println(letter); +} +~ } +~} +``` \ No newline at end of file diff --git a/src/methods.md b/src/methods.md index e1ead8b..3781962 100644 --- a/src/methods.md +++ b/src/methods.md @@ -4,7 +4,7 @@ All the code you have seen up until this point has lived inside of `void main() ```java void main() { - System.out.println("CODE GO HERE"); + IO.println("CODE GO HERE"); } ``` diff --git a/src/methods/arguments.md b/src/methods/arguments.md index 64eaf15..2d0fd38 100644 --- a/src/methods/arguments.md +++ b/src/methods/arguments.md @@ -9,7 +9,7 @@ to that method. These arguments let the caller of the method change what happens ```java void sayHello(String name) { - System.out.println("Hello " + name + "!"); + IO.println("Hello " + name + "!"); } void main() { diff --git a/src/methods/declaration.md b/src/methods/declaration.md index 1913d55..f762e8b 100644 --- a/src/methods/declaration.md +++ b/src/methods/declaration.md @@ -4,7 +4,7 @@ The simplest kind of method is declared by writing `void` followed by some name, ```java void doThing() { - System.out.println("Hello from inside a method!"); + IO.println("Hello from inside a method!"); } ~ ~void main() { diff --git a/src/methods/invocation.md b/src/methods/invocation.md index 3c41939..3e5abc9 100644 --- a/src/methods/invocation.md +++ b/src/methods/invocation.md @@ -5,7 +5,7 @@ name of the method followed by `()` in a statement. ```java void doThing() { - System.out.println("Hello from inside a method!"); + IO.println("Hello from inside a method!"); } void main() { @@ -19,7 +19,7 @@ You can call a method multiple times. If you do, then the code inside of it will ```java void doThing() { - System.out.println("Hello from inside a method!"); + IO.println("Hello from inside a method!"); } void main() { diff --git a/src/methods/main.md b/src/methods/main.md index 6eddbda..b718f05 100644 --- a/src/methods/main.md +++ b/src/methods/main.md @@ -5,7 +5,7 @@ call it in order to start your programs. ```java void main() { - System.out.println("Java will start here"); + IO.println("Java will start here"); } ``` @@ -19,6 +19,6 @@ void main() { return; } - System.out.println("WONT RUN"); + IO.println("WONT RUN"); } ``` diff --git a/src/methods/return.md b/src/methods/return.md index 2caa377..5ad9d9b 100644 --- a/src/methods/return.md +++ b/src/methods/return.md @@ -5,14 +5,14 @@ Anywhere inside of a method you can write `return` to immediately exit that meth ```java void willReturnEarly() { for (int i = 0; i < 10; i++) { - System.out.println(i); + IO.println(i); if (i == 5) { // Will stop at 5 because we exit the method return; } } - System.out.println("THIS WONT RUN"); + IO.println("THIS WONT RUN"); } void main() { diff --git a/src/methods/scope.md b/src/methods/scope.md index d20aa28..3bc24e7 100644 --- a/src/methods/scope.md +++ b/src/methods/scope.md @@ -6,9 +6,9 @@ Methods can contain any code, including variable declarations. void sayMathStuff() { int x = 1; int y = 2; - System.out.println("x is " + x); - System.out.println("y is " + y); - System.out.println("x + y is " + (x + y)); + IO.println("x is " + x); + IO.println("y is " + y); + IO.println("x + y is " + (x + y)); } void main() { @@ -23,15 +23,15 @@ Other code cannot see that variable. void sayMathStuff() { int x = 1; int y = 2; - System.out.println("x is " + x); - System.out.println("y is " + y); - System.out.println("x + y is " + (x + y)); + IO.println("x is " + x); + IO.println("y is " + y); + IO.println("x + y is " + (x + y)); } void main() { sayMathStuff(); // Error, x doesn't exist here - System.out.println(x); + IO.println(x); } ``` diff --git a/src/methods/void.md b/src/methods/void.md index 395eccd..3cfc088 100644 --- a/src/methods/void.md +++ b/src/methods/void.md @@ -15,7 +15,7 @@ int views() { // Doesn't return any value. void talkAboutVideo() { - System.out.println(title() + " only has " + views() + " views."); + IO.println(title() + " only has " + views() + " views."); } void main() { diff --git a/src/modules.md b/src/modules.md new file mode 100644 index 0000000..8c1581b --- /dev/null +++ b/src/modules.md @@ -0,0 +1,6 @@ +# Modules + +Every package in Java "lives" in a module. + +Just as classes may be grouped into packages, packages may be grouped into modules. + diff --git a/src/modules/declaration.md b/src/modules/declaration.md new file mode 100644 index 0000000..d35bf7b --- /dev/null +++ b/src/modules/declaration.md @@ -0,0 +1,32 @@ +# Declaration + +To declare a module, you need to create a file named `module-info.java` +and put it at the top of the folder where your code is. + +``` +src/ + example/ + Example.java + some/ + packageName/ + AClass.java + module-info.java +``` + +Within this file you put `module` followed by a name for the grouping of packages and `{}`.[^itsbest] + +```java +module example { +} +``` + +Just like with package names, module names can have multiple parts separated by `.`s. + +```java +module a.longer.name { +} +``` + +Nothing in this declaration says explicitly what packages are part of the module; it is just assumed that it holds the packages it is "next to." + +[^itsbest]: This name doesn't need to be related to the names of the packages, but whenever possible it's best to pick something that makes sense \ No newline at end of file diff --git a/src/modules/java.base.md b/src/modules/java.base.md new file mode 100644 index 0000000..bcfe2cf --- /dev/null +++ b/src/modules/java.base.md @@ -0,0 +1,50 @@ +# java.base + +The `java.base` module is where all packages like `java.lang`, `java.util`, etc. +are defined. + +This means it contains classes used by nearly every Java program like `java.lang.String`, +`java.lang.Integer`, and `java.util.ArrayList`. + +Because of this, it is the only module you do not need to explicitly require in a `module-info.java` file. + +```java +module cool.code { + // You can leave this line off + // and it will still require java.base + requires java.base; +} +``` + +And when you have a file that makes use of "The Anonymous Main class," +Java will also act as if you had a module import for `java.base`. This +means that you don't actually need an explicit import for classes like `ArrayList`. + +So if a file has the following + +```java +void main() { + var names = ArrayList(); + names.add("Him"); + names.add("Jim"); + names.add("Bim"); + IO.println(names); +} +``` + +It is equivalent to + +```java +import module java.base; + +class Main { + void main() { + var names = ArrayList(); + names.add("Him"); + names.add("Jim"); + names.add("Bim"); + IO.println(names); + } +} +``` + diff --git a/src/modules/module_imports.md b/src/modules/module_imports.md new file mode 100644 index 0000000..826b10d --- /dev/null +++ b/src/modules/module_imports.md @@ -0,0 +1,3 @@ +# Module Imports + +A special kind of import \ No newline at end of file diff --git a/src/modules/restrictions.md b/src/modules/restrictions.md new file mode 100644 index 0000000..e7569de --- /dev/null +++ b/src/modules/restrictions.md @@ -0,0 +1,10 @@ +# Restrictions + +All classes within a module must be in named packages + +The packages within one module must only be in that module. This +means that you cannot have a class in the `java.lang` package defined +in any of your modules. The `java.lang` + +Unlike packages, where two packages can have classes of the same name, +two modules cannot contain the same package. \ No newline at end of file diff --git a/src/modules/the_unnamed_module.md b/src/modules/the_unnamed_module.md new file mode 100644 index 0000000..8777a8f --- /dev/null +++ b/src/modules/the_unnamed_module.md @@ -0,0 +1,11 @@ +# The Unnamed Module + +All packages that are not in a named module are placed in "the unnamed module." +This includes all the code you have written thus far and all code written without +a `module-info.java` alongside it. + +This is generally okay. Modules as an organizational tool are most useful for sharing +code across "maintenance boundaries." Explicitly + +The unnamed module is special in that code within it `requires` every other module +and therefore can see every exported package and every class within. diff --git a/src/multi_dimensional_arrays/access_individual_elements.md b/src/multi_dimensional_arrays/access_individual_elements.md new file mode 100644 index 0000000..e1985ef --- /dev/null +++ b/src/multi_dimensional_arrays/access_individual_elements.md @@ -0,0 +1,23 @@ +# Access Individual Elements + +Accessing an element in a multi-dimensional array works the same as +accessing the elements of a one-dimensional array. + +Each time you index into it you will get out another array, so you just keep writing `[]` until +you've drilled down to an individual element. + +```java +~void main() { +String[][] ticTacToe = { + { "O", "", "O" }, + { "X", "O", "X" }, + { "O", "X", "X" } +} + +IO.println(ticTacToe[2][1]); + +// This is equivalent to the above +String[] row = ticTacToe[2]; +IO.println(row[1]); +~} +``` \ No newline at end of file diff --git a/src/multi_dimensional_arrays/array_initializers.md b/src/multi_dimensional_arrays/array_initializers.md new file mode 100644 index 0000000..337c3a4 --- /dev/null +++ b/src/multi_dimensional_arrays/array_initializers.md @@ -0,0 +1,16 @@ +# Array Initializers + +To give an initial value to a multi-dimensional array you can use multiple nested +array initializers. + +These are the same as normal array initializers but with potentially more initializers inside.[^moray] + +```java,no_run +String[][] ticTacToe = { + { "O", "", "O" }, + { "X", "O", "X" }, + { "O", "X", "X" } +} +``` + +[^moray]: When array initizalize and more initialize inside that's a moray. \ No newline at end of file diff --git a/src/multi_dimensional_arrays/declaration.md b/src/multi_dimensional_arrays/declaration.md new file mode 100644 index 0000000..76e587b --- /dev/null +++ b/src/multi_dimensional_arrays/declaration.md @@ -0,0 +1,16 @@ +# Declaration + +Declaring a multi-dimensional array is similar to declaring a normal array. + +The difference is that instead of only one `[]` following the type declaration, you add an extra `[]` +for each extra dimension. + +```java,no_run +// 2d array +int[][] grid; +// 3d array +int[][][] cube; +// 4d array +int[][][][] hypercube; +// ... and so on +``` \ No newline at end of file diff --git a/src/multi_dimensional_arrays/default_values.md b/src/multi_dimensional_arrays/default_values.md new file mode 100644 index 0000000..00ba2f1 --- /dev/null +++ b/src/multi_dimensional_arrays/default_values.md @@ -0,0 +1,19 @@ +# Default Values + +When a multi-dimensional array is made by just providing a size, its elements +are initialized to the same default values as they would be in a regular array. + +The difference is that each "nested array" will not be initialized to null. + +```java +~void main() { +String[][] ticTacToe = new String[3][3]; + +// Each array will be non-null +IO.println(ticTacToe[0]); + +// But the elements of those arrays will be null +// (or whatever the default value is for the type.) +IO.println(ticTacToe[0][0]); +~} +``` \ No newline at end of file diff --git a/src/multi_dimensional_arrays/initialization_with_new.md b/src/multi_dimensional_arrays/initialization_with_new.md new file mode 100644 index 0000000..3b85320 --- /dev/null +++ b/src/multi_dimensional_arrays/initialization_with_new.md @@ -0,0 +1,14 @@ +# Initialization with new + +Just like for one-dimensional arrays, adding `new` and the type of the array before an initializer +will let you use a multi-dimensional array as an expression or for a delayed assignment. + +```java +~void main() { +int[][] numbers; +numbers = new int[25][25]; + +// I think 25 is my favorite number +int length = (new int[25][25] {}).length; +~} +``` \ No newline at end of file diff --git a/src/multi_dimensional_arrays/initialize_with_size.md b/src/multi_dimensional_arrays/initialize_with_size.md new file mode 100644 index 0000000..4266ada --- /dev/null +++ b/src/multi_dimensional_arrays/initialize_with_size.md @@ -0,0 +1,11 @@ +# Initialization with Size + +Multi-dimensional arrays are used for representing multi-dimensional things. +These tend to also be large things for which writing out every value in an initializer is impractical. + +As such, you can initialize them with only an initial size for each dimension. + +```java +// Will make a 2d array of 160x144 booleans +boolean[][] pixels = new boolean[160][144]; +``` diff --git a/src/multi_dimensional_arrays/populate_values.md b/src/multi_dimensional_arrays/populate_values.md new file mode 100644 index 0000000..457be4d --- /dev/null +++ b/src/multi_dimensional_arrays/populate_values.md @@ -0,0 +1,20 @@ +# Populate Values + +Once you've created a multi-dimensional array you can use loops +to give initial values to its elements. + +Since you will end up nesting these loops, its a good example of a place where you should progress +naming your variables `i -> j -> k`. + +```java +// If you prefer true as the default over false +// you need to do that work yourself +boolean[][] booleanField = new boolean[25][25]; +for (int i = 0; i < booleanField.length; i++) { + for (int j = 0; j < booleanField[i].length; j++) { + booleanField[i][j] = true; + } +} +``` + +This is helpful if the default values (`null`, `0`, `false`, etc.) are not what you'd prefer \ No newline at end of file diff --git a/src/multi_dimensional_arrays/ragged_arrays.md b/src/multi_dimensional_arrays/ragged_arrays.md new file mode 100644 index 0000000..f118280 --- /dev/null +++ b/src/multi_dimensional_arrays/ragged_arrays.md @@ -0,0 +1,42 @@ +# Ragged Arrays + +In relatively unique circumstances[^alreadyunique] you might want to make a "ragged array". That is, a multi-dimensional +array where each array might be of a different size. + +You can do this by making the number of elements in nested initializers be different +```java +boolean[][] triangle = { + { false }, + { false, false, false}, + { false, false, false, false, false }, + { false, false, false}, + { false } +} +``` + +Or by using arrays initialized with `new` inside of an initializer. + +```java,no_run +boolean[][] triangle = { + new boolean[1], + new boolean[3], + new boolean[5], + new boolean[3], + new boolean[1] +}; +``` + +Or even by omitting the trailing dimensions on when initializing with new and later filling in each row. + +```java,no_run +boolean[][] triangle = new boolean[5][]; +triangle[0] = new boolean[1]; +triangle[1] = new boolean[3]; +triangle[2] = new boolean[5]; +triangle[3] = new boolean[3]; +triangle[4] = new boolean[1]; +``` + + + +[^alreadyunique]: And if you are using a multi-dimensional array, you are already doing something interesting. \ No newline at end of file diff --git a/src/multi_dimensional_arrays/set_individual_elements.md b/src/multi_dimensional_arrays/set_individual_elements.md new file mode 100644 index 0000000..00c3848 --- /dev/null +++ b/src/multi_dimensional_arrays/set_individual_elements.md @@ -0,0 +1,18 @@ +# Set Individual Elements + +You can set the value for elements in a multi-dimensional array the same way +as for one-dimensional arrays, just with an extra `[]` for each extra dimension. + +```java +String[][] ticTacToe = { + { "", "", "" }, + { "", "", "" }, + { "", "", "" } +} + +ticTacToe[0][0] = "X"; + +// The above is a shorthand for this +String[] row = ticTacToe[0]; +row[1] = "O"; +``` diff --git a/src/multi_file_programs/a_second_file.md b/src/multi_file_programs/a_second_file.md index 48e7370..ae71b7f 100644 --- a/src/multi_file_programs/a_second_file.md +++ b/src/multi_file_programs/a_second_file.md @@ -7,7 +7,7 @@ By this I mean, while in `Main.java` you are able to write something like this. ```java void sayHello() { - System.out.println("Hello"); + IO.println("Hello"); } void main() { @@ -36,7 +36,7 @@ Then from `Main.java` you can make an instance of `Ball` ```java void main() { var ball = new Ball(10); - System.out.println("The ball is " + ball.size + "cm across"); + IO.println("The ball is " + ball.size + "cm across"); } ``` diff --git a/src/multi_file_programs/global_fields.md b/src/multi_file_programs/global_fields.md index 9c707a7..6de7078 100644 --- a/src/multi_file_programs/global_fields.md +++ b/src/multi_file_programs/global_fields.md @@ -6,9 +6,9 @@ Global fields, accordingly, were always a lie. int number = 0; void main() { - System.out.println(number); + IO.println(number); number++; - System.out.println(number); + IO.println(number); } ``` @@ -19,9 +19,9 @@ class Main { int number = 0; void main() { - System.out.println(number); + IO.println(number); number++; - System.out.println(number); + IO.println(number); } } ``` diff --git a/src/multi_file_programs/the_anonymous_main_class.md b/src/multi_file_programs/the_anonymous_main_class.md index dc0d61c..7a1cff6 100644 --- a/src/multi_file_programs/the_anonymous_main_class.md +++ b/src/multi_file_programs/the_anonymous_main_class.md @@ -6,7 +6,7 @@ This includes the code in `Main.java`. ```java void main() { - System.out.println("What, really?"); + IO.println("What, really?"); } ``` @@ -20,7 +20,7 @@ If you take any code we've produced up until now and wrap it with `class Main {} ```java class Main { void main() { - System.out.println("yep."); + IO.println("yep."); } } ``` diff --git a/src/multi_file_programs/the_main_file.md b/src/multi_file_programs/the_main_file.md index eb550a5..1f15571 100644 --- a/src/multi_file_programs/the_main_file.md +++ b/src/multi_file_programs/the_main_file.md @@ -7,7 +7,7 @@ All the Java code you've written up until now will work if you put it into this ```java void main() { - System.out.println("Hello, world!"); + IO.println("Hello, world!"); } ``` diff --git a/src/niche_numerics.md b/src/niche_numerics.md new file mode 100644 index 0000000..a41e9df --- /dev/null +++ b/src/niche_numerics.md @@ -0,0 +1,19 @@ +# Niche Numerics + +For a surprisingly wide range of programs `int` and `double` +(along with their boxed counterparts `Integer` and `Double`) +are the only numeric types you will need. + +Every now and then, however, you will want something more exotic[^wording]. + +```java +~void main() { +byte b = 123; + +IO.println(b); +~} +``` + +[^wording]: The reason I am calling these "exotic" and "niche" has nothing to do with how +useful they are. If you want a `byte` then a `byte` is what you want. Its just that most +of the time (in my experience) you can get by with a little help from your `int`s. \ No newline at end of file diff --git a/src/niche_numerics/byte.md b/src/niche_numerics/byte.md new file mode 100644 index 0000000..e1161e9 --- /dev/null +++ b/src/niche_numerics/byte.md @@ -0,0 +1,64 @@ +# byte + +A `byte` represents a signed value between `-128` +and `127`. + +```java +~void main() { +byte a = 127; +IO.println(a); +byte b = -128; +IO.println(b); +~} +``` + +Operations like `+` and `*` on a `byte` will "promote" the result an `int` +and you will need to cast the result. Going from an `int` to a `byte` +is a narrowing conversion. + +```java +~void main() { +byte a = 5; +byte b = 6; +// Need to cast the result to a (byte) again +byte c = (byte) (a * b); +IO.println(c); +~} +``` + +Conversely, going from a `byte` to an `int` is a widening conversion and you won't +need a cast. + +```java +~void main() { +byte a = 5; +int a2 = a; // Widening conversion +IO.println(a2); +~} +``` + + +And if you have need of a potentially nullable `byte`, `Byte` with a capital `B` is the boxed version. + +```java +~void main() { +// Can't have a null "byte" +// byte b = null; + +// But you can have a null "Byte" +Byte b = null; +IO.println(b); +~} +``` + +You will most often want a `byte` when you are trying to save space in memory. + +```java,no_run +// This array of 4 bytes +byte[] bytes = { 1, 2, 3, 4 }; +// Will take up as much space as this +// array with 1 int +int[] oneInt = { 1 }; +``` + + diff --git a/src/niche_numerics/float.md b/src/niche_numerics/float.md new file mode 100644 index 0000000..39399a3 --- /dev/null +++ b/src/niche_numerics/float.md @@ -0,0 +1,63 @@ +# float + +What `int` is to `long`, `float` is to `double`. Even the type name `double` implicitly means "double the size of a float." So if you want something half the size of a `double` you can use a `float`. + +To write a float in a program you need to write `f` at the end of the floating +point literal. + +```java +~void main() { +float f = 3.5f; +IO.println(f); +~} +``` + +This is because Java sees floating point literals without a trailing `f` as representing a `double`. + +```java,does_not_compile +~void main() { +float f = 3.5; +IO.println(f); +~} +``` + +Conversions from a `double` to a `float` are narrowing and require an explicit cast. +Conversions from a `float` to a `double` are widening and do not require a cast. + +```java +~void main() { +double a = 6.5; +// Need a cast +float b = (float) a; +IO.println(b); + +float c = 9.5f; +// Do not need a cast +double d = c; +IO.println(d); +~} +``` + +And if you have need of a potentially nullable `float`, `Float` with a capital `F` is the boxed version. + +```java +~void main() { +// Can't have a null "float" +// float f = null; + +// But you can have a null "Float" +Float f = null; +IO.println(f); +~} +``` + +You will really only want a `float` when you are trying to save space in memory. +Otherwise its best to just use a `double`. + +```java,no_run +// This array of 2 floats +float[] floats = { 1.0f, 2.0f }; +// Will take up as much space as this +// array with 1 double +double[] oneDouble = { 1.0 }; +``` \ No newline at end of file diff --git a/src/niche_numerics/long.md b/src/niche_numerics/long.md new file mode 100644 index 0000000..154777b --- /dev/null +++ b/src/niche_numerics/long.md @@ -0,0 +1,74 @@ +# long + +If an `int` is not big enough for your needs, a `long` is twice as big as an `int` +and can represent numbers from `-2^63` to `2^63 - 1`. + +You can make a `long` from an integer literal, but integer literals do not +normally allow for numbers that an `int` cannot store. + +```java,does_not_compile +~void main() { +// Smaller numbers work without issue +long smallNumber = 5; +IO.println(smallNumber); +// This is too big for an int +long bigNumber = 55555555555; +IO.println(bigNumber); +~} +``` + +For those cases you need to add an `L` to the end of the literal.[^lforlong] + +```java +~void main() { +long smallNumber = 5; +IO.println(smallNumber); +// But with an L at the end, its not too big for a long +long bigNumber = 55555555555L; +IO.println(bigNumber); +~} +``` + +All operations with a `long` will result in a `long`. Conversions to `int` and +other "smaller" integer types will be narrowing and require a cast. + +```java +~void main() { +long a = 5; +int b = 3; +// a long times an int will result in a long +long c = a * b; +IO.println(c); +~} +``` + +And if you have need of a potentially nullable `long`, `Long` with a capital `L` is the boxed version. + +```java +~void main() { +// Can't have a null "long" +// long l = null; + +// But you can have a null "Long" +Long l = null; +IO.println(l); +~} +``` + +The reason you will likely end up using `int` more than `long` is that `int` +works more with other parts of Java. Like array indexing - you can't +get an item in an array with a `long`, you need an `int` for that. + +```java,does_not_compile +~void main() { +String[] sadRobots = { "2B", "9S", "A2" }; +long index = 2; +// Longs can't be used as indexes +String sadRobot = sadRobots[index]; +~} +``` + +But there is nothing wrong with a `long`. If you need to represent a number that is potentially bigger than an `int` then it is useful. + + +[^lforlong]: "L is for long" would be a total cop-out in a children's picture book. \ No newline at end of file diff --git a/src/niche_numerics/short.md b/src/niche_numerics/short.md new file mode 100644 index 0000000..123a9a7 --- /dev/null +++ b/src/niche_numerics/short.md @@ -0,0 +1,87 @@ +# short + +A `short` represents a signed value between `-32768` +and `32767`. Representing a `short` takes twice as much memory as representing +a `byte` and half as much memory as representing an `int`. + +```java +~void main() { +short a = 32767; +IO.println(a); +byte b = -32768; +IO.println(b); +~} +``` + +Operations like `+` and `*` on a `short` will "promote" the result an `int` +and you will need to cast the result. Going from an `int` to a `short` +is a narrowing conversion. + +```java +~void main() { +short a = 5; +short b = 6; +// Need to cast the result to a (byte) again +short c = (short) (a * b); +IO.println(c); +~} +``` + +Conversely, going from a `short` to an `int` is a widening conversion and you won't +need a cast. + +```java +~void main() { +short a = 5; +int a2 = a; // Widening conversion +IO.println(a2); +~} +``` + + +And if you have need of a potentially nullable `short`, `Short` with a capital `S` is the boxed version. + +```java +~void main() { +// Can't have a null "short" +// short b = null; + +// But you can have a null "Short" +Short b = null; +IO.println(b); +~} +``` + + +A `short` also takes up exactly as much space as a `char` and converting between the two +is allowed, but will still require an explicit cast in both directions.[^neither] + +```java +~void main() { +short s = 50; +char c = (char) s; +s = (short) c; +IO.println(c); +~} +``` + +You will most often want a `short` when you are trying to save space in memory but +need to represent numbers beyond what a `byte` can represent.[^rare] + +```java,no_run +// This array of 2 shorts +short[] shorts = { 1, 2 }; + +// Will take up as much space as this +// array with 1 int +int[] oneInt = { 1 }; + +// And as much space as this array with 4 bytes +byte[] bytes = { 1, 2, 3, 4 }; +``` + + +[^neither]: These are neither narrowing or widening conversions. Java just makes you put +the cast. One way to view this is that when you go from a `short` to a `char` you've transformed a "number" into a "character." So while no information is lost, what the information "represents" has changed. + +[^rare]: As you might suspect, this is more rare than the situations where you would want a `byte`. \ No newline at end of file diff --git a/src/niche_numerics/unsigned_operations.md b/src/niche_numerics/unsigned_operations.md new file mode 100644 index 0000000..93d3b10 --- /dev/null +++ b/src/niche_numerics/unsigned_operations.md @@ -0,0 +1,41 @@ +# Unsigned Operations + +While a `byte` represents a signed value between `-128` +and `127`, its not uncommon to instead want to represent a number between `0` and `255`. +We call representations like that, where we repurpose what would be negative numbers to instead be large positive numbers, "unsigned." + +All of Java's built-in numeric types are signed, but you can use static methods +to perform operations on them that work as if they were unsigned. + +```java +~void main() { +// Java sees 255 as being out of range for a byte +// so we have to cast +byte b = (byte) 255; +// And by default this will be seen as -1 +IO.println(b); +// But we can use Byte.toUnsignedInt to see it +// as 255 +IO.println(Byte.toUnsignedInt(b)); +~} +``` + +Operations like addition with `+` don't need special unsigned versions since they work "the same" regardless of whether you ultimately choose to interpret your numbers as unsigned or signed. Other operations like division or comparisons need special logic +to work right when doing "unsigned math." + +```java +~void main() { +int i = -1; +// -1 is actually 4294967295 when viewed as an unsigned int +IO.println(Integer.toUnsignedString(i)); +// So normal comparisons won't do what you want +boolean isFiveBigger = 5 > i; +IO.println(isFiveBigger); +// You'd want to use special unsigned comparisons +isFiveBigger = Integer.compareUnsigned(5, i) > 0; +IO.println(isFiveBigger); +~} +``` + +All these special unsigned operations are `static` methods on each type's corresponding +boxed equivalent class. What static methods are available varies from type to type. \ No newline at end of file diff --git a/src/null.md b/src/null.md index 7b6af59..f5ebb22 100644 --- a/src/null.md +++ b/src/null.md @@ -7,8 +7,8 @@ void main() { String name = null; int[] numbers = null; - System.out.println(name); - System.out.println(numbers); + IO.println(name); + IO.println(numbers); } ``` diff --git a/src/null/challenges.md b/src/null/challenges.md index 86f5f9f..4dcd216 100644 --- a/src/null/challenges.md +++ b/src/null/challenges.md @@ -66,7 +66,7 @@ void main() { }; for (int i = 0; i < jobs.length; i++) { - System.out.println(jobs[i]); + IO.println(jobs[i]); } } ``` @@ -86,7 +86,7 @@ void main() { }; for (int i = 0; i < numbers.length; i++) { - System.out.println(numbers[i]); + IO.println(numbers[i]); } } ``` @@ -116,27 +116,27 @@ String bigness(String letters) { } void main() { - System.out.println( + IO.println( bigness("bore") ); - System.out.println( + IO.println( bigness("boiler") ); - System.out.println( + IO.println( bigness("filter") ); - System.out.println( + IO.println( bigness("knower") ); - System.out.println( + IO.println( bigness("chrysanthemum") ); - System.out.println( + IO.println( bigness(null) ); } diff --git a/src/null/checking_for_null.md b/src/null/checking_for_null.md index acf7782..5c36527 100644 --- a/src/null/checking_for_null.md +++ b/src/null/checking_for_null.md @@ -6,10 +6,10 @@ check by using `==`. ```java void sayHello(String firstName, String lastName) { if (lastName == null) { - System.out.println("Hello " + firstName); + IO.println("Hello " + firstName); } else { - System.out.println("Hello " + firstName + " " + lastName); + IO.println("Hello " + firstName + " " + lastName); } } diff --git a/src/null/null_as_absence.md b/src/null/null_as_absence.md index 3b80916..d9f8cc4 100644 --- a/src/null/null_as_absence.md +++ b/src/null/null_as_absence.md @@ -13,7 +13,7 @@ Cher does not have a last name. String firstName = "Cher"; String lastName = null; -System.out.println(firstName); -System.out.println(lastName); +IO.println(firstName); +IO.println(lastName); ~} ``` diff --git a/src/null/null_pointer_exception.md b/src/null/null_pointer_exception.md index 76a46bf..2799baa 100644 --- a/src/null/null_pointer_exception.md +++ b/src/null/null_pointer_exception.md @@ -8,7 +8,7 @@ crash. void main() { String thing = null; // NullPointerException - System.out.println(thing.length()); + IO.println(thing.length()); } ``` diff --git a/src/objects/equals_and_hashCode.md b/src/objects/equals_and_hashCode.md index f256e5f..6a45732 100644 --- a/src/objects/equals_and_hashCode.md +++ b/src/objects/equals_and_hashCode.md @@ -16,15 +16,15 @@ class Main { var t1 = new Thing(); var t2 = new Thing(); - System.out.println(t1 == t1); - System.out.println(t1.equals(t1)); + IO.println(t1 == t1); + IO.println(t1.equals(t1)); - System.out.println(t2 == t2); - System.out.println(t2.equals(t2)); + IO.println(t2 == t2); + IO.println(t2.equals(t2)); - System.out.println(t1 == t2); - System.out.println(t1.equals(t2)); + IO.println(t1 == t2); + IO.println(t1.equals(t2)); } } ``` @@ -39,8 +39,8 @@ class Main { Integer b = 3; Integer c = 4; - System.out.println(a.equals(b)); - System.out.println(a.equals(c)); + IO.println(a.equals(b)); + IO.println(a.equals(c)); } } ``` @@ -62,18 +62,18 @@ class Main { String b = "abc"; String c = "bca"; - System.out.println(a.hashCode()); + IO.println(a.hashCode()); // a.equals(b) will return true, so they will have the same hash code - System.out.println(b.hashCode()); + IO.println(b.hashCode()); // a.equals(c) will return false, so they may or may not have the same hash code - System.out.println(c.hashCode()); + IO.println(c.hashCode()); Thing t1 = new Thing(); Thing t2 = new Thing(); // The default .equals() is the same as == - System.out.println(t1.hashCode()); - System.out.println(t2.hashCode()); + IO.println(t1.hashCode()); + IO.println(t2.hashCode()); } } ``` diff --git a/src/objects/instanceof.md b/src/objects/instanceof.md index 9a204cb..0129b19 100644 --- a/src/objects/instanceof.md +++ b/src/objects/instanceof.md @@ -8,7 +8,7 @@ of the data stored in it using `instanceof`. Object o = "123"; if (o instanceof String) { - System.out.println("This object is a String!"); + IO.println("This object is a String!"); } ~} ``` @@ -27,7 +27,7 @@ unavailable when all Java knows is that you have an `Object`. Object o = "123"; if (o instanceof String s) { - System.out.println( + IO.println( "Can call String methods after recovering the type: " + s.charAt(0) ); } diff --git a/src/objects/override.md b/src/objects/override.md index d28e56e..3573479 100644 --- a/src/objects/override.md +++ b/src/objects/override.md @@ -21,7 +21,7 @@ class Position { void main() { Object o = new Position(9, 8); - System.out.println(o); + IO.println(o); } ``` @@ -52,6 +52,6 @@ class Position { void main() { Object o = new Position(9, 8); - System.out.println(o); + IO.println(o); } ``` \ No newline at end of file diff --git a/src/objects/override_toString.md b/src/objects/override_toString.md index 7a16323..2b0b030 100644 --- a/src/objects/override_toString.md +++ b/src/objects/override_toString.md @@ -20,11 +20,11 @@ class Window { void main() { Object o = new Window(); - System.out.println(o); + IO.println(o); } ``` -This is how you can customize the output of `System.out.println`. +This is how you can customize the output of `IO.println`. It is common practice for a class holding data to include the values of its fields in its `toString` representation. @@ -47,6 +47,6 @@ class Position { void main() { Object o = new Position(9, 8); - System.out.println(o); + IO.println(o); } ``` \ No newline at end of file diff --git a/src/objects/subtypes.md b/src/objects/subtypes.md index ef42e6b..2db2de3 100644 --- a/src/objects/subtypes.md +++ b/src/objects/subtypes.md @@ -10,7 +10,7 @@ you can assign any data you want into it. ~void main() { String oak = "oak"; Object tree = oak; -System.out.println(tree); +IO.println(tree); ~} ``` diff --git a/src/objects/toString.md b/src/objects/toString.md index da0d526..4d554e8 100644 --- a/src/objects/toString.md +++ b/src/objects/toString.md @@ -15,17 +15,17 @@ Object o = "123"; // If its already a String, toString() doesn't // have to do much work -System.out.println(o.toString()); +IO.println(o.toString()); o = 123; // Integers, Longs, etc. all have a representation // which looks the same as they do in literal form. -System.out.println(o.toString()); +IO.println(o.toString()); o = new Apple(); // And custom classes will, by default, just have the // class name followed by gibberish -System.out.println(o.toString()); +IO.println(o.toString()); ~} ``` diff --git a/src/packages/package_private_methods.md b/src/packages/package_private_methods.md index d42f927..54a3725 100644 --- a/src/packages/package_private_methods.md +++ b/src/packages/package_private_methods.md @@ -11,7 +11,7 @@ package village; public class Villager { void isNotVisible() { - System.out.println(""" + IO.println(""" This method can be called from code in the 'village' package, but not from other packages. """); diff --git a/src/packages/public_constructors.md b/src/packages/public_constructors.md index 7f65830..1b4fc10 100644 --- a/src/packages/public_constructors.md +++ b/src/packages/public_constructors.md @@ -26,7 +26,7 @@ class Main { var skeleton = new Skeleton(); // And we get the right number of bones! - System.out.println(skeleton.bones); + IO.println(skeleton.bones); } } ``` diff --git a/src/packages/public_methods.md b/src/packages/public_methods.md index 87c48f3..86ca2a6 100644 --- a/src/packages/public_methods.md +++ b/src/packages/public_methods.md @@ -9,11 +9,11 @@ package village; // Now other packages will be able to see it public class Villager { public void isVisible() { - System.out.println("This method is callable from another package."); + IO.println("This method is callable from another package."); } void isNotVisible() { - System.out.println("This method is not.") + IO.println("This method is not.") } } ``` @@ -25,7 +25,7 @@ package village; public class Well { public static int drawWater() { - System.out.println(""" + IO.println(""" You need this to be both public and static to be able to write Well.drawWater() """); diff --git a/src/packages/the_anonymous_main_class.md b/src/packages/the_anonymous_main_class.md index 56617e1..9347010 100644 --- a/src/packages/the_anonymous_main_class.md +++ b/src/packages/the_anonymous_main_class.md @@ -6,7 +6,7 @@ inside the default package. ```java,no_run // Allowed void main() { - System.out.println("Hello, world"); + IO.println("Hello, world"); } ``` @@ -15,7 +15,7 @@ void main() { package myprogram; void main() { - System.out.println("Hello, world"); + IO.println("Hello, world"); } ``` @@ -28,7 +28,7 @@ package myprogram; class Main { void main() { - System.out.println("Hello, world"); + IO.println("Hello, world"); } } ``` \ No newline at end of file diff --git a/src/records/check_for_equality.md b/src/records/check_for_equality.md index 70d4cc1..9d185bb 100644 --- a/src/records/check_for_equality.md +++ b/src/records/check_for_equality.md @@ -13,7 +13,7 @@ class Main { var elfOne = new Elf(true); var elfTwo = new Elf(true); - System.out.println(elfOne.equals(elfTwo)); + IO.println(elfOne.equals(elfTwo)); } } ``` diff --git a/src/records/component_accessor_visibility.md b/src/records/component_accessor_visibility.md index 488be93..4d9f072 100644 --- a/src/records/component_accessor_visibility.md +++ b/src/records/component_accessor_visibility.md @@ -14,7 +14,7 @@ import dungeon.Dragon; void main() { var dragon = new Dragon(224.5); - System.out.println( + IO.println( // Method is visible. dragon.wingspan() ); diff --git a/src/records/component_accessors.md b/src/records/component_accessors.md index 05910c9..5bfa0f7 100644 --- a/src/records/component_accessors.md +++ b/src/records/component_accessors.md @@ -18,7 +18,7 @@ class Main { // .name() accessor is available String name = dog.name(); - System.out.println(name); + IO.println(name); } } ``` diff --git a/src/records/printing_a_record.md b/src/records/printing_a_record.md index 48ebc85..245e5cb 100644 --- a/src/records/printing_a_record.md +++ b/src/records/printing_a_record.md @@ -12,7 +12,7 @@ class Main { void main() { var goblin = new Goblin("Gobbo", 11); - System.out.println(goblin); + IO.println(goblin); } } ``` diff --git a/src/records/return_multiple_values.md b/src/records/return_multiple_values.md index 88ad0e9..3be89ed 100644 --- a/src/records/return_multiple_values.md +++ b/src/records/return_multiple_values.md @@ -14,7 +14,7 @@ Location findTreasureIsland() { void main() { Location treasureIsland = findTreasureIsland(); - System.out.println( + IO.println( "Treasure island is located at " + treasureIsland.latitude() + " " + diff --git a/src/recursion.md b/src/recursion.md index 13763d7..71cd54e 100644 --- a/src/recursion.md +++ b/src/recursion.md @@ -4,11 +4,11 @@ In a method you can call another method. ```java void doOtherThing() { - System.out.println("B"); + IO.println("B"); } void doThing() { - System.out.println("A"); + IO.println("A"); } void main() { @@ -23,7 +23,7 @@ What might not be obvious is that you can call the method currently running. ```java void countDown(int value) { - System.out.println(value); + IO.println(value); if (value > 0) { countDown(value - 1); } diff --git a/src/recursion/accumulators.md b/src/recursion/accumulators.md index 37cbe4b..26cae06 100644 --- a/src/recursion/accumulators.md +++ b/src/recursion/accumulators.md @@ -16,7 +16,7 @@ class Main { } void main() { - System.out.println( + IO.println( timesTwo(4) ); } @@ -46,11 +46,11 @@ class Main { } int timesTwo(int x) { - timesTwo(x, 0); + return timesTwo(x, 0); } void main() { - System.out.println( + IO.println( timesTwo(4) ); } diff --git a/src/recursion/comparison_to_delegation.md b/src/recursion/comparison_to_delegation.md index 9ccd004..cc45409 100644 --- a/src/recursion/comparison_to_delegation.md +++ b/src/recursion/comparison_to_delegation.md @@ -2,14 +2,14 @@ A related technique to recursion is "delegation." -This is when you have one method call a different airity +This is when you have one method call a different arity version of itself. ```java // This method is delegated to void seasonFood(int shakes) { for (int i = 0; i < shakes; i++) { - System.out.println("1 shake of pepper"); + IO.println("1 shake of pepper"); } } diff --git a/src/recursion/comparison_to_loops.md b/src/recursion/comparison_to_loops.md index 63f3c34..14f63ce 100644 --- a/src/recursion/comparison_to_loops.md +++ b/src/recursion/comparison_to_loops.md @@ -11,21 +11,21 @@ void seasonFoodRecursive(int times) { return; } else { - System.out.println("seasoning"); + IO.println("seasoning"); seasonFoodRecursive(times - 1); } } void seasonFoodIterative(int times) { for (int i = 0; i < times; i++) { - System.out.println("seasoning"); + IO.println("seasoning"); } } ~ ~void main() { -~ System.out.println("Recursive"); +~ IO.println("Recursive"); ~ seasonFoodRecursive(2); -~ System.out.println("Iterative"); +~ IO.println("Iterative"); ~ seasonFoodRecursive(2); ~} ``` diff --git a/src/recursion/counting_down.md b/src/recursion/counting_down.md index 3fa7e1f..5e397bd 100644 --- a/src/recursion/counting_down.md +++ b/src/recursion/counting_down.md @@ -9,10 +9,10 @@ number one lower than you were given. if it isn't you are done. ```java void countDown(int x) { if (x > 0) { - System.out.println("DONE"); + IO.println("DONE"); } else { - System.out.println("x: " + x); + IO.println("x: " + x); countDown(x - 1); } } @@ -31,10 +31,10 @@ All of this is equivalent to a `while` loop that looks like the following. ```java void countDown(int x) { while (x > 0) { - System.out.println("x: " + x); + IO.println("x: " + x); x = x - 1; } - System.out.println("DONE"); + IO.println("DONE"); } void main() { diff --git a/src/recursion/recursing_over_arrays.md b/src/recursion/recursing_over_arrays.md index f5b4949..b31d90d 100644 --- a/src/recursion/recursing_over_arrays.md +++ b/src/recursion/recursing_over_arrays.md @@ -1 +1,27 @@ # Recurse Over an Array + +To write a recursive function which acts over each element +of an array, the technique is the same as with you need to do much the same task as with regular loops, +just by having your "current index" be a parameter to the function. + +```java +class Main { + void printEachTimesEight(int[] nums, int i) { + if (i < nums.length) { + IO.println(nums[i] * 8); + printEachTimesEight(nums, i + 1); + } + } + + void printEachTimesEight(int[] nums) { + printEachTimesEight(nums, 0); + } + + void main() { + printEachTimesEight(new int[] { 1, 2, 3 }); + } +} +``` + +This same general technique can be used to loop over other sorts of collections, like `ArrayList`. In that +case you would use `.get()` and `.size()` instead of `[]` and `.length`, but the concept is the same. \ No newline at end of file diff --git a/src/recursion/recursing_over_strings.md b/src/recursion/recursing_over_strings.md index 7bad604..ad32aa1 100644 --- a/src/recursion/recursing_over_strings.md +++ b/src/recursion/recursing_over_strings.md @@ -2,4 +2,25 @@ To write a recursive function which acts over each character of a `String` you need to do much the same task as with regular loops, -just by having your "current index" be a parameter to the \ No newline at end of file +just by having your "current index" be a parameter to the function. + +```java +class Main { + void printEachUpperCase(String s, int i) { + if (i < s.length()) { + IO.println(Character.toUpperCase(s.charAt(i))); + printEachUpperCase(s, i + 1); + } + } + + void printEachUpperCase(String s) { + printEachUpperCase(s, 0); + } + + void main() { + printEachUpperCase("hello"); + } +} +``` + +This overload with the index is an example of a function taking an accumulator. \ No newline at end of file diff --git a/src/reflection/class_objects.md b/src/reflection/class_objects.md index 4ee12d3..d8f0479 100644 --- a/src/reflection/class_objects.md +++ b/src/reflection/class_objects.md @@ -12,7 +12,7 @@ special thing. class Main { void main() { Class stringClass = String.class; - System.out.println(stringClass); + IO.println(stringClass); } } ``` @@ -29,7 +29,7 @@ class Main { void main() { String s = "Hello"; Class stringClass = s.getClass(); - System.out.println(stringClass); + IO.println(stringClass); } } ``` diff --git a/src/reflection/get_a_constructor.md b/src/reflection/get_a_constructor.md new file mode 100644 index 0000000..c179d46 --- /dev/null +++ b/src/reflection/get_a_constructor.md @@ -0,0 +1,50 @@ +# Get a Constructor + +Following the pattern, `getConstructor` gets a reference to a `Constructor` object. +Just like `getMethod`, this requires specifying argument types. + +Since `getField` might throw a `NoSuchFieldException` and `getMethod` might +throw a `NoSuchMethodException` you might expect a `getConstructor` to throw +a `NoSuchConstructorException`. It does not. If there is no match for the constructor +you are trying to find it will reuse `NoSuchMethodException`. + +`Constructor` objects are also similar to `Class` objects in that they can carry a generic parameter +specifying what kind of object will be made when they are invoked. + +```java +import java.lang.reflect.Constructor; + +class Main { + void main() throws NoSuchMethodException { + Class airplaneFoodClass = AirplaneFood.class; + + // Zero argument constructor. + // Note that we have Constructor. + // If you have a Class it will give you a Constructor + Constructor constructor + = airplaneFoodClass.getConstructor(); + + IO.println(constructor); + + // One argument constructor + constructor = airplaneFoodClass.getConstructor(boolean.class); + + IO.println(constructor); + } +} + +class AirplaneFood { + public final boolean tastesGood; + + public AirplaneFood() { + this.tastesGood = false; + } + + public AirplaneFood(boolean tastesGood) { + if (tastesGood) { + throw new RuntimeException("Lies"); + } + this.tastesGood = false; + } +} +``` \ No newline at end of file diff --git a/src/reflection/get_a_field.md b/src/reflection/get_a_field.md new file mode 100644 index 0000000..5415351 --- /dev/null +++ b/src/reflection/get_a_field.md @@ -0,0 +1,47 @@ +# Get a Field + +You can retrieve a single field by its name using `getField`. If there +is no field with that name it will throw a `NoSuchFieldException`. + +```java +import java.lang.reflect.Field; + +class Main { + void main() throws NoSuchFieldException { + Class drinkClass = Drink.class; + + Field nameField = drinkClass.getField("name"); + IO.println(nameField); + } +} + +class Drink { + public String name; + public boolean caffeinated; +} +``` + +And if you need to access a field that might be non-public you can use `getDeclaredField`. + +```java,panics +import java.lang.reflect.Field; + +class Main { + void main() throws NoSuchFieldException { + Class soupClass = Soup.class; + + Field hasVeggiesField = soupClass.getDeclaredField("hasVeggies"); + IO.println(hasVeggiesField); + + // Will fail. getField won't see hasVeggies + soupClass.getField("hasVeggies"); + } +} + +class Soup { + public String name; + boolean isChicken; + private boolean hasVeggies; +} +``` + diff --git a/src/reflection/get_a_method.md b/src/reflection/get_a_method.md new file mode 100644 index 0000000..da1dc44 --- /dev/null +++ b/src/reflection/get_a_method.md @@ -0,0 +1,87 @@ +# Get a Method + +To get a specific method from a class you can use `getMethod`. If there is no method that matches the name and argument types a `NoSuchMethodException` will be thrown. [^nosuchmethod] + +```java +import java.lang.reflect.Method; + +class Main { + void main() throws NoSuchMethodException { + Class teaClass = Tea.class; + + Method sipMethod = teaClass.getMethod("sip"); + IO.println(sipMethod); + } +} + +class Tea { + public void sip() { + } +} +``` + + +Unlike fields which can be identified only by their name, methods which are distinct overloads of eachother +are distinguised by the arguments they take in. + +```java +import java.lang.reflect.Method; + +class Main { + void main() throws NoSuchMethodException { + Class teaClass = Tea.class; + + // There is a sip method which takes zero arguments + Method sipMethod = teaClass.getMethod("sip"); + IO.println(sipMethod); + + // which is a different method than + // sip that takes one int + sipMethod = teaClass.getMethod("sip", int.class); + IO.println(sipMethod); + + // which is a different method than + // sip that takes a String and an int + sipMethod = teaClass.getMethod("sip", String.class, int.class); + IO.println(sipMethod); + } +} + +class Tea { + public void sip() { + } + + public void sip(int numberOfSips) { + } + + public void sip(String baristaName, int numberOfSips) { + } +} +``` + +And, as you might imagine, `getDeclaredMethod` will do the same thing with the distinction +of seeing non-public methods. + +```java +class Main { + void main() throws NoSuchMethodException { + Class fruitClass = Fruit.class; + + IO.println(fruitClass.getDeclaredMethod("bite")); + IO.println(fruitClass.getDeclaredMethod("chew")); + IO.println(fruitClass.getDeclaredMethod("swallow")); + } +} + +class Fruit { + public void bite() { + } + + void chew() { + } + + private void swallow() { + } +} +``` + diff --git a/src/reflection/get_all_constructors.md b/src/reflection/get_all_constructors.md new file mode 100644 index 0000000..2792672 --- /dev/null +++ b/src/reflection/get_all_constructors.md @@ -0,0 +1,41 @@ +# Get all Constructors + +If you want a list of all constructors, `.getConstructors` will give you that. + +What is worth noting is that unlike `.getConstructor` the array returned from this +will not have the generic filled in.[^genericarrays] + +```java +import java.lang.reflect.Constructor; + +class Main { + void main() { + Class airplaneFoodClass = AirplaneFood.class; + + Constructor[] constructors + = airplaneFoodClass.getConstructors(); + + for (Constructor constructor : constructors) { + IO.println(constructor); + } + } +} + +class AirplaneFood { + public final boolean tastesGood; + + public AirplaneFood() { + this.tastesGood = false; + } + + public AirplaneFood(boolean tastesGood) { + if (tastesGood) { + throw new RuntimeException("Lies"); + } + this.tastesGood = false; + } +} +``` + +[^genericarrays]: Arrays of generic objects are, in general, a hairy topic. Java can't really ensure that you use them right, +so there a lot of restrictions on making them. You can't make an `ArrayList[]`, for example. \ No newline at end of file diff --git a/src/reflection/get_all_fields.md b/src/reflection/get_all_fields.md index 42063e6..d586ebb 100644 --- a/src/reflection/get_all_fields.md +++ b/src/reflection/get_all_fields.md @@ -2,7 +2,7 @@ If you have a class object, you can get all the public fields of that class using `getFields`. This gives you -an array of field objects. +an array of `Field` objects. ```java import java.lang.reflect.Field; @@ -13,7 +13,7 @@ class Main { Field[] fields = drinkClass.getFields(); for (var field : fields) { - System.out.println(field.getName()); + IO.println(field.getName()); } } } @@ -35,18 +35,18 @@ class Main { void main() { Class soupClass = Soup.class; - System.out.println("Using getFields"); + IO.println("Using getFields"); Field[] publicFields = soupClass.getFields(); for (var field : publicFields) { - System.out.println(field.getName()); + IO.println(field.getName()); } - System.out.println("-------------"); + IO.println("-------------"); - System.out.println("Using getDeclaredFields"); + IO.println("Using getDeclaredFields"); Field[] allFields = soupClass.getDeclaredFields(); for (var field : allFields) { - System.out.println(field.getName()); + IO.println(field.getName()); } } } diff --git a/src/reflection/get_all_methods.md b/src/reflection/get_all_methods.md new file mode 100644 index 0000000..4845298 --- /dev/null +++ b/src/reflection/get_all_methods.md @@ -0,0 +1,71 @@ +# Get all Methods + + +If you have a class object, you can get all the public +methods of that class using `getMethods`. This gives you +an array of `Method` objects. + +Note that this will also include available methods that come from `java.lang.Object`. +In addition to `toString`, `equals`, and `hashCode` you will see a few you don't recognize. +All in due time. + +```java +import java.lang.reflect.Method; + +class Main { + void main() { + Class teaClass = Tea.class; + + Method[] methods = teaClass.getMethods(); + for (var method : methods) { + IO.println(method); + } + } +} + +class Tea { + public void sip() { + } + + public void gulp() { + } +} +``` + +Just like there is `getDeclaredFields` for seeing non-public fields, `getDeclaredMethods` will +give you all methods, regardless of their visibility. + +```java +import java.lang.reflect.Method; + +class Main { + void main() { + Class fruitClass = Fruit.class; + + IO.println("Using getMethods"); + Method[] publicMethods = fruitClass.getMethods(); + for (var method : publicMethods) { + IO.println(method); + } + + IO.println("-------------"); + + IO.println("Using getDeclaredMethods"); + Method[] allMethods = fruitClass.getDeclaredMethods(); + for (var method : allMethods) { + IO.println(method); + } + } +} + +class Fruit { + public void bite() { + } + + void chew() { + } + + private void swallow() { + } +} +``` \ No newline at end of file diff --git a/src/reflection/get_methods.md b/src/reflection/get_methods.md new file mode 100644 index 0000000..07222f4 --- /dev/null +++ b/src/reflection/get_methods.md @@ -0,0 +1 @@ +# Get Methods diff --git a/src/reflection/invoke_a_constructor.md b/src/reflection/invoke_a_constructor.md new file mode 100644 index 0000000..241bc04 --- /dev/null +++ b/src/reflection/invoke_a_constructor.md @@ -0,0 +1,48 @@ +# Invoke a Constructor + +Just as you can get and set a field using the `.get` and `.set` methods on a `Field` and just as you can invoke +a method by using the `.invoke` method on a `Method`, you may also +invoke constructors with `.newInstance` on a `Constructor` object. + +```java +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +class Main { + void main() throws + InvocationTargetException, + InstantiationException, + IllegalAccessException, + NoSuchMethodException { + Class airplaneFoodClass = AirplaneFood.class; + + Constructor constructor + = airplaneFoodClass.getConstructor(); + + AirplaneFood airplaneFood = constructor.newInstance(); + + IO.println(airplaneFood.tastesGood); + + constructor = airplaneFoodClass.getConstructor(boolean.class); + + airplaneFood = constructor.newInstance(false); + + IO.println(airplaneFood.tastesGood); + } +} + +class AirplaneFood { + public final boolean tastesGood; + + public AirplaneFood() { + this.tastesGood = false; + } + + public AirplaneFood(boolean tastesGood) { + if (tastesGood) { + throw new RuntimeException("Lies"); + } + this.tastesGood = false; + } +} +``` diff --git a/src/reflection/invoke_a_method.md b/src/reflection/invoke_a_method.md new file mode 100644 index 0000000..f50c3b7 --- /dev/null +++ b/src/reflection/invoke_a_method.md @@ -0,0 +1,68 @@ +# Invoke a Method + +Just as you can get and set a field using the `.get` and `.set` methods on a `Field`, you can invoke +a method by using the `.invoke` method on a `Method`. + + +For instance methods there is a first "hidden" argument that is the instance you would be invoking the method on. +This needs to be the first argument to `.invoke`. + +```java +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +class Main { + void main() throws IllegalAccessException, InvocationTargetException { + Class teaClass = Tea.class; + + // sip taking zero arguments + Method sipMethod; + try { + sipMethod = teaClass.getMethod("sip", int.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + var tea = new Tea(); + + sipMethod.invoke(tea, 5); + } +} + +class Tea { + public void sip(int numberOfSips) { + IO.println("You made " + numberOfSips + " sips"); + } +} +``` + +For static methods you do not need an instance of the class to invoke them. +Instead you need to pass the class itself as the first argument to `.invoke`. + +```java +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +class Main { + void main() throws IllegalAccessException, InvocationTargetException { + Class appleClass = Apple.class; + + // sip taking zero arguments + Method biteMethod; + try { + biteMethod = appleClass.getMethod("bite", int.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + biteMethod.invoke(Apple.class, 5); + biteMethod.invoke(Apple.class, 1); + } +} + +class Apple { + public static void bite(int times) { + IO.println("You took " + times + " bite" + (times < 1 ? "." : "s.")); + } +} +``` \ No newline at end of file diff --git a/src/reflection/read_from_a_field.md b/src/reflection/read_from_a_field.md new file mode 100644 index 0000000..ed17138 --- /dev/null +++ b/src/reflection/read_from_a_field.md @@ -0,0 +1,40 @@ +# Read from a Field + +Once you have a `Field` object you can use its `get` method to read the value of that field +from an object. This will throw an `IllegalAccessException` if you try to read a field you +are not allowed to.[^permission] + +```java +import java.lang.reflect.Field; + +class Main { + void main() throws IllegalAccessException { + Class drinkClass = Drink.class; + + Field nameField; + try { + nameField = drinkClass.getField("name"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); // Should have this field + } + + var soda = new Drink("Soda", true); + var water = new Drink("Water", false); + + IO.println(nameField.get(soda)); + IO.println(nameField.get(water)); + } +} + +class Drink { + public String name; + public boolean caffeinated; + + Drink(String name, boolean caffeinated) { + this.name = name; + this.caffeinated = caffeinated; + } +} +``` + +[^permission]: The rules for this go a bit beyond "you cannot read private fields." \ No newline at end of file diff --git a/src/reflection/write_to_a_field.md b/src/reflection/write_to_a_field.md new file mode 100644 index 0000000..5fe056f --- /dev/null +++ b/src/reflection/write_to_a_field.md @@ -0,0 +1,114 @@ +# Write to a Field + +Similarly, you can write to a field using the `.set` method on a `Field` object. +This will also throw `IllegalAccessException` if its not something you are allowed to do. + +```java,panics +import java.lang.reflect.Field; + +class Main { + void main() throws IllegalAccessException { + Class drinkClass = Drink.class; + + Field caffeinatedField; + try { + caffeinatedField = drinkClass.getField("caffeinated"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + + var water = new Drink("Water", false); + + // You can put drugs in anything you set your mind to, kids + caffeinatedField.set(water, true); + + IO.println(caffeinatedField.get(water)); + } +} + +class Drink { + public String name; + public boolean caffeinated; + + Drink(String name, boolean caffeinated) { + this.name = name; + this.caffeinated = caffeinated; + } +} +``` + +If you try to set a field to the wrong type of value - like setting a `boolean` field to have a `String` value - +you will get an `IllegalArgumentException`. Unlike `NoSuchFieldException` and `IllegalAccessException` this is an +unchecked exception, so you do not need to explicitly account for it. + + +```java,panics +import java.lang.reflect.Field; + +class Main { + void main() throws IllegalAccessException { + Class drinkClass = Drink.class; + + Field caffeinatedField; + try { + caffeinatedField = drinkClass.getField("caffeinated"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + + var soda = new Drink("Soda", true); + + caffeinatedField.set(soda, "yes, very much so"); + + IO.println(caffeinatedField.get(soda)); + } +} + +class Drink { + public String name; + public boolean caffeinated; + + Drink(String name, boolean caffeinated) { + this.name = name; + this.caffeinated = caffeinated; + } +} +``` + +The same will happen if you try to set a `final` field.[^finalfields] + +```java,panics +import java.lang.reflect.Field; + +class Main { + void main() throws IllegalAccessException { + Class drinkClass = Drink.class; + + Field nameField; + try { + nameField = drinkClass.getField("name"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); // Should have this field + } + + var water = new Drink("Water", false); + + // You can put drugs in anything you set your mind to, kids + nameField.set(water, true); + + IO.println(nameField.get(water)); + } +} + +class Drink { + public String name; + public final boolean caffeinated; + + Drink(String name, boolean caffeinated) { + this.name = name; + this.caffeinated = caffeinated; + } +} +``` + +[^finalfields]: There are especially evil ways to actually change `final` fields using reflection. If you want to know how to do that I'm not telling you. \ No newline at end of file diff --git a/src/return_values/challenges.md b/src/return_values/challenges.md index 41d093c..0bf53fd 100644 --- a/src/return_values/challenges.md +++ b/src/return_values/challenges.md @@ -17,7 +17,7 @@ How the value for that int is determined is up to you. void main() { int x = process("abc"); - System.out.println("Got " + x); + IO.println("Got " + x); } ``` @@ -48,11 +48,11 @@ int multiply(int x, int y) { } void main() { - System.out.println(multiply(3, 5)); + IO.println(multiply(3, 5)); - // System.out.println(multiply(-5, 5)); - // System.out.println(multiply(-5, -2)); - // System.out.println(multiply(9, -2)); + // IO.println(multiply(-5, 5)); + // IO.println(multiply(-5, -2)); + // IO.println(multiply(9, -2)); } ``` @@ -79,7 +79,7 @@ void main() { int y = 8; int z = subtractInt(add(4, 5), multiply(4, 2)); - System.out.println(z); + IO.println(z); } ``` diff --git a/src/return_values/return_in_void_methods.md b/src/return_values/return_in_void_methods.md index 31d5330..77e30bf 100644 --- a/src/return_values/return_in_void_methods.md +++ b/src/return_values/return_in_void_methods.md @@ -11,7 +11,7 @@ void doStuff() { return; } - System.out.println(i); + IO.println(i); } } ``` diff --git a/src/return_values/return_values.md b/src/return_values/return_values.md index e7477db..8a5b4b6 100644 --- a/src/return_values/return_values.md +++ b/src/return_values/return_values.md @@ -9,7 +9,7 @@ int returnsEight() { void main() { int value = returnsEight(); - System.out.println(value); + IO.println(value); } ``` @@ -21,6 +21,6 @@ String returnsName() { } void main() { - System.out.println(returnsName() + " is my name"); + IO.println(returnsName() + " is my name"); } ``` diff --git a/src/return_values/unreachable_statements.md b/src/return_values/unreachable_statements.md index b2f014b..f24f7c1 100644 --- a/src/return_values/unreachable_statements.md +++ b/src/return_values/unreachable_statements.md @@ -5,10 +5,10 @@ If Java can figure out that some line of code is unreachable because it will alw ```java void doThing() { - System.out.println("A"); + IO.println("A"); return; // unreachable statement - System.out.println("B"); + IO.println("B"); } void main() { @@ -20,11 +20,11 @@ Java is easy to trick though.[^trick] ```java void doThing() { - System.out.println("A"); + IO.println("A"); if (true) { return; } - System.out.println("B"); + IO.println("B"); } void main() { diff --git a/src/return_values/void.md b/src/return_values/void.md index 3080a06..ba16473 100644 --- a/src/return_values/void.md +++ b/src/return_values/void.md @@ -15,7 +15,7 @@ int views() { // Doesn't return any value. void talkAboutVideo() { - System.out.println(title() + " only has " + views() + " views."); + IO.println(title() + " only has " + views() + " views."); } // This is what the void in "void main()" means diff --git a/src/standard_input.md b/src/standard_input.md index 928a99a..64a59fc 100644 --- a/src/standard_input.md +++ b/src/standard_input.md @@ -4,17 +4,3 @@ Programs are pretty boring if they just make a machine warm and do some math. There are a lot of ways to make a program interactive, but the easiest is to read from what is called "standard input." - -This function will be added to Java in a future version, but until then -copy paste this at the very top of all the code in this section. - -```java,no_run -import java.util.Scanner; - -Scanner scanner = new Scanner(System.in); - -String input(String message) { - System.out.print(message); - return scanner.nextLine(); -} -``` \ No newline at end of file diff --git a/src/standard_input/delayed_assignment.md b/src/standard_input/delayed_assignment.md index b6d5618..0c51470 100644 --- a/src/standard_input/delayed_assignment.md +++ b/src/standard_input/delayed_assignment.md @@ -9,54 +9,38 @@ One strategy for this is to declare any response-holding variables outside the l The problem with this is that Java isn't smart enough to know that you always initialize those variables. ```java,no_run,does_not_compile -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { String name; while (true) { - String name = input("What is your name? "); + String name = IO.readln("What is your name? "); if (name.isBlank()) { - System.out.println("Name cannot be blank!"); + IO.println("Name cannot be blank!"); continue; } break; } - System.out.println("Hello " + name); + IO.println("Hello " + name); } ``` To get around this you can either give an explicit default value. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { String name = null; while (true) { - String name = input("What is your name? "); + String name = IO.readln("What is your name? "); if (name.isBlank()) { - System.out.println("Name cannot be blank!"); + IO.println("Name cannot be blank!"); continue; } break; } - System.out.println("Hello " + name); + IO.println("Hello " + name); } ``` @@ -65,27 +49,18 @@ In this context our old friend delayed assignment becomes an option again. This to see that the code in the loop will run at least once. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { String name; do { - String name = input("What is your name? "); + String name = IO.readln("What is your name? "); if (name.isBlank()) { - System.out.println("Name cannot be blank!"); + IO.println("Name cannot be blank!"); continue; } break; } while (true); - System.out.println("Hello " + name); + IO.println("Hello " + name); } ``` \ No newline at end of file diff --git a/src/standard_input/interpreting_input.md b/src/standard_input/interpreting_input.md index 657c9bd..6574357 100644 --- a/src/standard_input/interpreting_input.md +++ b/src/standard_input/interpreting_input.md @@ -1,6 +1,6 @@ # Interpreting Input -When you call `input` the human on the other side can type whatever they want. +When you call `IO.readln` the human on the other side can type whatever they want. This means that, depending on the question you asked, you might need to interpret what they typed as something other than a generic `String`. @@ -8,18 +8,9 @@ what they typed as something other than a generic `String`. In its most basic form this will look like seeing if one `String` equals another `String`. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { while (true) { - String shouldExit = input("Exit the program? (y/n)"); + String shouldExit = IO.readln("Exit the program? (y/n)"); if (shouldExit.equals("y")) { break; } diff --git a/src/standard_input/leniency.md b/src/standard_input/leniency.md index 61e97f9..3689af7 100644 --- a/src/standard_input/leniency.md +++ b/src/standard_input/leniency.md @@ -7,24 +7,17 @@ This means accounting for common mistakes people make like having extra spaces o For this purpose, methods like `strip` and `equalsIgnoreCase` are useful. ```java,no_run -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { while (true) { - String response = input("Answer me: yes or no").strip(); + String response = IO.readln("Answer me: yes or no").strip(); if (response.equalsIgnoreCase("yes")) { - System.out.println("aight"); + IO.println("aight"); } else if (response.equalsIgnoreCase("no")) { - System.out.println("cool"); + IO.println("cool"); } else { - System.out.println("try again"); + IO.println("try again"); continue; } diff --git a/src/standard_input/prompting.md b/src/standard_input/prompting.md index d6595cf..b096a57 100644 --- a/src/standard_input/prompting.md +++ b/src/standard_input/prompting.md @@ -1,25 +1,18 @@ # input -To prompt a user for information you use the `input` function. +To prompt a user for information you use the `IO.readln` function. -The `input` function takes a `String` to output as a prompt. This will -work the same as if the `String` was passed to `System.out.print`. +`IO.readln` takes a `String` to output as a prompt. This will +work the same as if the `String` was passed to `IO.print`. The program will then wait until a human types some text and clicks the enter key. Whatever they typed will be returned to the program as a `String`. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { - String name = input("What is your name? "); - System.out.println("Hello, " + name); + String name = IO.readln("What is your name? "); + IO.println("Hello, " + name); } -``` \ No newline at end of file +``` + +`readln` stands for "read line." It reads the next line a person types. \ No newline at end of file diff --git a/src/standard_input/reprompting.md b/src/standard_input/reprompting.md index 54fe71c..a1ce2c8 100644 --- a/src/standard_input/reprompting.md +++ b/src/standard_input/reprompting.md @@ -6,26 +6,17 @@ This is a good use case for loops. You ask a question and, if the answer you get you proceed as normal. If it is not then you loop back and ask again. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { while (true) { - String response = input("Answer me: yes or no"); + String response = IO.readln("Answer me: yes or no"); if (response.equals("yes")) { - System.out.println("okay then!"); + IO.println("okay then!"); } else if (response.equals("no")) { - System.out.println("also fine!"); + IO.println("also fine!"); } else { - System.out.println("Not a valid response"); + IO.println("Not a valid response"); // Will go back to the top of the loop continue; } diff --git a/src/standard_input/transporting_data.md b/src/standard_input/transporting_data.md index cb1748c..a911f30 100644 --- a/src/standard_input/transporting_data.md +++ b/src/standard_input/transporting_data.md @@ -4,20 +4,11 @@ If you ask someone multiple questions you likely will get multiple variables worth of information. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { - String firstName = input("What is your first name? "); - String lastName = input("What is your last name? "); + String firstName = IO.readln("What is your first name? "); + String lastName = IO.readln("What is your last name? "); - System.out.println("Hello " + firstName + " " + lastName + "."); + IO.println("Hello " + firstName + " " + lastName + "."); } ``` @@ -25,22 +16,13 @@ This is fine and dandy so long as you immediately use those variables. But once reprompting logic code can get pretty lengthy. ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ void main() { String firstName; do { - firstName = input("What is your first name? "); + firstName = IO.readln("What is your first name? "); if (firstName.isBlank()) { - System.out.println("First name cannot be blank."); + IO.println("First name cannot be blank."); } else { break; @@ -49,17 +31,17 @@ void main() { String lastName; do { - lastName = input("What is your first name? "); + lastName = IO.readln("What is your first name? "); if (lastName.isBlank()) { - System.out.println("First name cannot be blank."); + IO.println("First name cannot be blank."); } else { break; } } while (true); - System.out.println("Hello " + firstName + " " + lastName + "."); + IO.println("Hello " + firstName + " " + lastName + "."); } ``` @@ -69,15 +51,6 @@ I mention all this as a reminder that when you want to return multiple values fr you can use a class.[^dto] ```java,no_run -~import java.util.Scanner; -~ -~Scanner scanner = new Scanner(System.in); -~ -~String input(String message) { -~ System.out.print(message); -~ return scanner.nextLine(); -~} -~ class Person { String firstName; String lastName; @@ -91,10 +64,10 @@ class Person { Person askForName() { String firstName; do { - firstName = input("What is your first name? "); + firstName = IO.readln("What is your first name? "); if (firstName.isBlank()) { - System.out.println("First name cannot be blank."); + IO.println("First name cannot be blank."); } else { break; @@ -103,10 +76,10 @@ Person askForName() { String lastName; do { - lastName = input("What is your first name? "); + lastName = IO.readln("What is your first name? "); if (lastName.isBlank()) { - System.out.println("First name cannot be blank."); + IO.println("First name cannot be blank."); } else { break; @@ -119,7 +92,7 @@ Person askForName() { void main() { Person person = askForName(); - System.out.println("Hello " + person.firstName + " " + person.lastName + "."); + IO.println("Hello " + person.firstName + " " + person.lastName + "."); } ``` diff --git a/src/static_fields/initialization.md b/src/static_fields/initialization.md index 68a5068..182336d 100644 --- a/src/static_fields/initialization.md +++ b/src/static_fields/initialization.md @@ -11,8 +11,8 @@ class Main { static String name; void main() { - System.out.println(count); // 0 - System.out.println(name); // null + IO.println(count); // 0 + IO.println(name); // null } } ``` @@ -28,8 +28,8 @@ class Main { static String name = "bob"; void main() { - System.out.println(count); // 5 - System.out.println(name); // bob + IO.println(count); // 5 + IO.println(name); // bob } } ``` @@ -48,8 +48,8 @@ class Main { } void main() { - System.out.println(count); // 5 - System.out.println(name); // bob + IO.println(count); // 5 + IO.println(name); // bob } } ``` diff --git a/src/static_fields/usage.md b/src/static_fields/usage.md index 5cf5cb6..8cb5998 100644 --- a/src/static_fields/usage.md +++ b/src/static_fields/usage.md @@ -7,7 +7,7 @@ class Main { static int count = 0; void main() { - System.out.println(count); + IO.println(count); } } ``` @@ -21,7 +21,7 @@ class Main { static int count = 0; void main() { - System.out.println(Main.count); + IO.println(Main.count); } } ``` \ No newline at end of file diff --git a/src/static_methods.md b/src/static_methods.md index 7a1ee48..c779f2a 100644 --- a/src/static_methods.md +++ b/src/static_methods.md @@ -19,7 +19,7 @@ class MyMath { class Main { void main() { int result = MyMath.add(1, 2); - System.out.println(result); + IO.println(result); } } ``` \ No newline at end of file diff --git a/src/static_methods/factories.md b/src/static_methods/factories.md index 02e0818..c8a79fa 100644 --- a/src/static_methods/factories.md +++ b/src/static_methods/factories.md @@ -65,7 +65,7 @@ class Position { Using a static method to create a `Position` - i.e. as a "factory" - is a way around the issue.[^note] -```java,does_not_compile +```java class Position { int x; int y; @@ -109,9 +109,9 @@ class Main { var p2 = Position.fromX(4); var p3 = Position.fromY(5); - System.out.println(p1.x + ", " + p1.y); - System.out.println(p2.x + ", " + p2.y); - System.out.println(p3.x + ", " + p3.y); + IO.println(p1.x + ", " + p1.y); + IO.println(p2.x + ", " + p2.y); + IO.println(p3.x + ", " + p3.y); } } ``` diff --git a/src/static_methods/scope.md b/src/static_methods/scope.md index 3852c42..cfbf82f 100644 --- a/src/static_methods/scope.md +++ b/src/static_methods/scope.md @@ -12,7 +12,7 @@ class ScopeExample { static void doStuff() { canCall(); - System.out.println(ScopeExample.CAN_ACCESS); + IO.println(ScopeExample.CAN_ACCESS); } } ``` @@ -28,7 +28,7 @@ class ScopeExample2 { static void doStuff() { cannotCall(); - System.out.println( + IO.println( CANNOT_ACCESS ); } diff --git a/src/static_methods/usage.md b/src/static_methods/usage.md index 65ee21b..c317c23 100644 --- a/src/static_methods/usage.md +++ b/src/static_methods/usage.md @@ -6,7 +6,7 @@ defined followed by `.` and the method name. ```java class StuffDoer { static void doStuff() { - System.out.println("Doing stuff"); + IO.println("Doing stuff"); } } ``` @@ -14,7 +14,7 @@ class StuffDoer { ```java ~class StuffDoer { ~ static void doStuff() { -~ System.out.println("Doing stuff"); +~ IO.println("Doing stuff"); ~ } ~} ~ diff --git a/src/streams.md b/src/streams.md new file mode 100644 index 0000000..80eeccb --- /dev/null +++ b/src/streams.md @@ -0,0 +1 @@ +# Streams diff --git a/src/streams/collectors.md b/src/streams/collectors.md new file mode 100644 index 0000000..06840d4 --- /dev/null +++ b/src/streams/collectors.md @@ -0,0 +1 @@ +# Collectors diff --git a/src/streams/filter.md b/src/streams/filter.md new file mode 100644 index 0000000..e816ea1 --- /dev/null +++ b/src/streams/filter.md @@ -0,0 +1 @@ +# filter diff --git a/src/streams/gatherers.md b/src/streams/gatherers.md new file mode 100644 index 0000000..2bae9a3 --- /dev/null +++ b/src/streams/gatherers.md @@ -0,0 +1 @@ +# Gatherers diff --git a/src/streams/map.md b/src/streams/map.md new file mode 100644 index 0000000..d4ab730 --- /dev/null +++ b/src/streams/map.md @@ -0,0 +1 @@ +# map diff --git a/src/streams/mapMulti.md b/src/streams/mapMulti.md new file mode 100644 index 0000000..76ab5b3 --- /dev/null +++ b/src/streams/mapMulti.md @@ -0,0 +1 @@ +# mapMulti diff --git a/src/streams/toList.md b/src/streams/toList.md new file mode 100644 index 0000000..fe773bb --- /dev/null +++ b/src/streams/toList.md @@ -0,0 +1 @@ +# toList diff --git a/src/string_builder.md b/src/string_builder.md index e47ec59..efc1a74 100644 --- a/src/string_builder.md +++ b/src/string_builder.md @@ -8,7 +8,7 @@ void main() { String b = "b"; String ab = a + b; - System.out.println(ab); + IO.println(ab); } ``` diff --git a/src/stringbuilder.md b/src/stringbuilder.md index 0886a44..5c97234 100644 --- a/src/stringbuilder.md +++ b/src/stringbuilder.md @@ -22,7 +22,7 @@ void main() { String screenplay = "5".repeat(1000000); // There are 1301 characters in the above text, meaning this method // does around that many copies of the string - System.out.println(removeAllNumbers(screenplay)); + IO.println(removeAllNumbers(screenplay)); } ``` @@ -42,6 +42,6 @@ void main() { String screenplay = "5".repeat(1000000); // There are 1301 characters in the above text, meaning this method // does around that many copies of the string - System.out.println(removeAllNumbers(screenplay)); + IO.println(removeAllNumbers(screenplay)); } ``` \ No newline at end of file diff --git a/src/strings/access_individual_characters.md b/src/strings/access_individual_characters.md index 336cbfb..c1f2978 100644 --- a/src/strings/access_individual_characters.md +++ b/src/strings/access_individual_characters.md @@ -11,16 +11,16 @@ The second by using `1`, and so on. String spy = "loid"; char l = spy.charAt(0); -System.out.println(l); +IO.println(l); char o = spy.charAt(1); -System.out.println(o); +IO.println(o); char i = spy.charAt(2); -System.out.println(i); +IO.println(i); char d = spy.charAt(3); -System.out.println(d); +IO.println(d); ~} ``` @@ -34,7 +34,7 @@ String assassin = "yor"; int indexOfR = 2; char r = assassin.charAt(indexOfR); -System.out.println(r); +IO.println(r); ~} ``` diff --git a/src/strings/challenges.md b/src/strings/challenges.md index 51d9fde..b0a5da7 100644 --- a/src/strings/challenges.md +++ b/src/strings/challenges.md @@ -15,7 +15,7 @@ void main() { String second = "2"; String result = first + second; - System.out.println(result); + IO.println(result); } ``` @@ -28,7 +28,7 @@ void main() { String first = "1"; int second = 2; - System.out.println(first + second); + IO.println(first + second); } ``` @@ -43,7 +43,7 @@ void main() { String second = "b"; String third = "ab"; - System.out.println((first + second).equals(third)); + IO.println((first + second).equals(third)); } ``` @@ -59,7 +59,7 @@ void main() { char a = 'a'; char b = 'b'; char c = 'c'; - System.out.println(a + b + c); + IO.println(a + b + c); } ``` @@ -78,19 +78,19 @@ void main() { int index = 6; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.print(racecar.charAt(index)); + IO.print(racecar.charAt(index)); index += diff; - System.out.println(racecar.charAt(index)); + IO.println(racecar.charAt(index)); } ``` diff --git a/src/strings/common_escape_sequences.md b/src/strings/common_escape_sequences.md index 3199d90..cf2ac00 100644 --- a/src/strings/common_escape_sequences.md +++ b/src/strings/common_escape_sequences.md @@ -21,7 +21,7 @@ String title = "The \"Honorable\" Judge Judy"; Since the backslash is used to escape characters, it too needs to escaped in order to have it be in a `String`. So to encode `¯\_(ツ)_/¯` into a String -you need to escape the first backslash. +you need to escape the first backslash.[^forwardslash] ```java ~void main() { @@ -37,3 +37,5 @@ And much the same as with `char`, you need to use `\n` to write in a newline. String letter = "To Whom It May Concern,\n\nI am writing this letter to complain."; ~} ``` + +[^forwardslash]: We call `\` a "backslash" and `/` a "forward slash." In `¯\_(ツ)_/¯` the left arm is drawn using the backslash and the right arm with a forward slash. What makes left "backwards" and right "forwards" is just social norms. \ No newline at end of file diff --git a/src/strings/concatenation.md b/src/strings/concatenation.md index d78e560..6e7169e 100644 --- a/src/strings/concatenation.md +++ b/src/strings/concatenation.md @@ -9,7 +9,7 @@ String llo = "llo"; String hello = he + llo; -System.out.println(hello); +IO.println(hello); ~} ``` @@ -27,6 +27,6 @@ double dollars = 1.52; String message = "I have " + numberOfApples + " apples and $" + dollars + " in my pocket."; -System.out.println(message); +IO.println(message); ~} ``` diff --git a/src/strings/equality.md b/src/strings/equality.md index 8d13b24..6e5c5b9 100644 --- a/src/strings/equality.md +++ b/src/strings/equality.md @@ -10,8 +10,8 @@ String lyricTwo = "Green, Green, Dress"; boolean areSameLyric = lyricOne.equals(lyricTwo); boolean isMyName = lyricOne.equals("Bop Bop"); -System.out.println(areSameLyric); -System.out.println(isMyName); +IO.println(areSameLyric); +IO.println(isMyName); ~} ``` @@ -28,6 +28,6 @@ String wow = "WOW"; boolean areNotSame = !bow.equals(wow); -System.out.println(areNotSame); +IO.println(areNotSame); ~} ``` diff --git a/src/strings/length.md b/src/strings/length.md index 768a6eb..2a43400 100644 --- a/src/strings/length.md +++ b/src/strings/length.md @@ -8,7 +8,7 @@ String fruit = "strawberry"; int numberOfChars = fruit.length(); // strawberry is 10 characters long -System.out.println( +IO.println( fruit + " is " + numberOfChars + " characters long" ); ~} diff --git a/src/strings_ii/UPPERCASE.md b/src/strings_ii/UPPERCASE.md index 3a52ddd..870e39e 100644 --- a/src/strings_ii/UPPERCASE.md +++ b/src/strings_ii/UPPERCASE.md @@ -8,7 +8,7 @@ void main() { String message = "Happy Valentines Day"; String upperCased = message.toUpperCase(); - System.out.println(upperCased); + IO.println(upperCased); } ``` diff --git a/src/strings_ii/challenges.md b/src/strings_ii/challenges.md index 9cac1a8..1503c5d 100644 --- a/src/strings_ii/challenges.md +++ b/src/strings_ii/challenges.md @@ -22,11 +22,11 @@ boolean isUpperCase(String s) { void main() { // true - System.out.println(isUpperCase("ABC")); + IO.println(isUpperCase("ABC")); // false - System.out.println(isUpperCase("abc")); + IO.println(isUpperCase("abc")); // false - System.out.println(isUpperCase("AbC")); + IO.println(isUpperCase("AbC")); } ``` @@ -44,11 +44,11 @@ boolean isLowerCase(String s) { void main() { // false - System.out.println(isLowerCase("ABC")); + IO.println(isLowerCase("ABC")); // true - System.out.println(isLowerCase("abc")); + IO.println(isLowerCase("abc")); // false - System.out.println(isLowerCase("AbC")); + IO.println(isLowerCase("AbC")); } ``` @@ -72,11 +72,11 @@ void main() { kermit.name = "kermit"; // kermit - System.out.println(kermit.name); + IO.println(kermit.name); // KERMIT! kermit.scream(); - System.out.println(kermit.name); + IO.println(kermit.name); } ``` diff --git a/src/strings_ii/check_if_blank.md b/src/strings_ii/check_if_blank.md index 253a61c..ab31bcf 100644 --- a/src/strings_ii/check_if_blank.md +++ b/src/strings_ii/check_if_blank.md @@ -14,9 +14,9 @@ void main() { """; // false - System.out.println(brainSounds.isEmpty()); + IO.println(brainSounds.isEmpty()); // true - System.out.println(brainSounds.isBlank()); + IO.println(brainSounds.isBlank()); } ``` \ No newline at end of file diff --git a/src/strings_ii/check_if_empty.md b/src/strings_ii/check_if_empty.md index 5060647..5edfa58 100644 --- a/src/strings_ii/check_if_empty.md +++ b/src/strings_ii/check_if_empty.md @@ -8,7 +8,7 @@ and see if that is zero. ```java void main() { String textMessages = ""; - System.out.println( + IO.println( textMessages.length() == 0 ); } @@ -19,7 +19,7 @@ But another is to use the explicitly defined `.isEmpty()` method. ```java void main() { String textMessages = ""; - System.out.println( + IO.println( textMessages.isEmpty() ); } diff --git a/src/strings_ii/equality_ignoring_case.md b/src/strings_ii/equality_ignoring_case.md index b5d3af2..f7abc7b 100644 --- a/src/strings_ii/equality_ignoring_case.md +++ b/src/strings_ii/equality_ignoring_case.md @@ -9,7 +9,7 @@ void main() { String historicalFigureOne = "St. Valentines"; String historicalFigureTwo = "st. valentines"; - System.out.println( + IO.println( historicalFigureOne.equalsIgnoreCase(historicalFigureTwo) ); } diff --git a/src/strings_ii/lowercase.md b/src/strings_ii/lowercase.md index cbc5585..d928474 100644 --- a/src/strings_ii/lowercase.md +++ b/src/strings_ii/lowercase.md @@ -10,7 +10,7 @@ void main() { String message = "Happy Valentines Day"; String lowerCased = message.toLowerCase(); - System.out.println(lowerCased); + IO.println(lowerCased); } ``` diff --git a/src/strings_ii/strip_extra_whitespace.md b/src/strings_ii/strip_extra_whitespace.md index 424aa82..d9ac03a 100644 --- a/src/strings_ii/strip_extra_whitespace.md +++ b/src/strings_ii/strip_extra_whitespace.md @@ -9,8 +9,8 @@ This will give a new `String` with both the leading and trailing whitespace remo void main() { String message = " Happy Valentines Day. "; - System.out.print(message.strip()); - System.out.println("|"); + IO.print(message.strip()); + IO.println("|"); } ``` @@ -20,8 +20,8 @@ If you want to just remove the leading whitespace, you can use `.stripLeading`. void main() { String message = " Happy Valentines Day. "; - System.out.print(message.stripLeading()); - System.out.println("|"); + IO.print(message.stripLeading()); + IO.println("|"); } ``` @@ -31,8 +31,8 @@ And to remove only trailing whitespace, `.stripTrailing`. void main() { String message = " Happy Valentines Day. "; - System.out.print(message.stripTrailing()); - System.out.println("|"); + IO.print(message.stripTrailing()); + IO.println("|"); } ``` diff --git a/src/switch.md b/src/switch.md index 6e65d24..2a41376 100644 --- a/src/switch.md +++ b/src/switch.md @@ -17,16 +17,16 @@ But it can be burdensome if all you are doing is checking if some variable has a ```java ~void main() { if (food.equals("apple")) { - System.out.println("Red"); + IO.println("Red"); } else if (name.equals("grape")) { - System.out.println("Purple"); + IO.println("Purple"); } else if (food.equals("orange")) { - System.out.println("Orange"); + IO.println("Orange"); } else { - System.out.println("Other"); + IO.println("Other"); } ~} ``` @@ -36,16 +36,16 @@ For these situations, you can use a `switch`. ```java switch (fruit) { case "apple" -> { - System.out.println("Red"); + IO.println("Red"); } case "grape" -> { - System.out.println("Purple"); + IO.println("Purple"); } case "orange" -> { - System.out.println("Orange"); + IO.println("Orange"); } default -> { - System.out.println("Other"); + IO.println("Other"); } } ``` diff --git a/src/switch/case_and_default.md b/src/switch/case_and_default.md index 163f1e1..faaf441 100644 --- a/src/switch/case_and_default.md +++ b/src/switch/case_and_default.md @@ -13,16 +13,16 @@ and gives you a place to put "default" behavior. void sayColor(String fruit) { switch (fruit) { case "apple" -> { - System.out.println("Red"); + IO.println("Red"); } case "grape" -> { - System.out.println("Purple"); + IO.println("Purple"); } case "orange" -> { - System.out.println("Orange"); + IO.println("Orange"); } default -> { - System.out.println("Other"); + IO.println("Other"); } } } diff --git a/src/switch/challenges.md b/src/switch/challenges.md index 61f5b6e..8d3983d 100644 --- a/src/switch/challenges.md +++ b/src/switch/challenges.md @@ -19,15 +19,15 @@ boolean isSorcerer(String name) { } void main() { - System.out.println( + IO.println( isSorcerer("yuji") ); - System.out.println( + IO.println( isSorcerer("gojo") ); - System.out.println( + IO.println( isSorcerer("yugi") // Wrong series ); } @@ -47,19 +47,19 @@ boolean didRedSoxWin(int year) { } void main() { - System.out.println( + IO.println( "2004: " + didRedSoxWin(2004) ); - System.out.println( + IO.println( "1998: " + didRedSoxWin(1998) ); - System.out.println( + IO.println( "2013: " + didRedSoxWin(2013) ); - System.out.println( + IO.println( "1903: " + didRedSoxWin(1903) ); } @@ -92,25 +92,25 @@ StopLight transition(StopLight current) { void main() { var light = StopLight.RED; - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); } ``` @@ -136,25 +136,25 @@ StopLight transition(StopLight current) { void main() { var light = StopLight.RED; - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); light = transition(light); - System.out.println(light); + IO.println(light); } ``` @@ -189,28 +189,28 @@ Action inCaseOfBearAttack(Bear bear) { } void main() { - System.out.println( + IO.println( inCaseOfBearAttack(Bear.POLAR) ); - System.out.println( + IO.println( inCaseOfBearAttack(Bear.BROWN) ); - System.out.println( + IO.println( inCaseOfBearAttack(Bear.BLACK) ); - System.out.println( + IO.println( inCaseOfBearAttack(Bear.PANDA) ); - System.out.println( + IO.println( inCaseOfBearAttack(Bear.KOALA) ); - System.out.println( + IO.println( inCaseOfBearAttack(null) ); } diff --git a/src/switch/combining_cases.md b/src/switch/combining_cases.md index a260f50..5d02816 100644 --- a/src/switch/combining_cases.md +++ b/src/switch/combining_cases.md @@ -20,6 +20,6 @@ String scientificName(String vegetable) { } ~ ~void main() { -~ System.out.println(scientificName("cabbage")); +~ IO.println(scientificName("cabbage")); ~} ``` \ No newline at end of file diff --git a/src/switch/enums.md b/src/switch/enums.md index aea07a4..524ccc0 100644 --- a/src/switch/enums.md +++ b/src/switch/enums.md @@ -16,13 +16,13 @@ void main() { StopLight light = StopLight.GREEN; switch (light) { case RED -> { - System.out.println("Stop!"); + IO.println("Stop!"); } case YELLOW -> { - System.out.println("Speed up, coward!"); + IO.println("Speed up, coward!"); } case GREEN -> { - System.out.println("Go!"); + IO.println("Go!"); } } } diff --git a/src/switch/ints.md b/src/switch/ints.md index db49609..5057919 100644 --- a/src/switch/ints.md +++ b/src/switch/ints.md @@ -7,13 +7,13 @@ You can also use `int`s with switches. int year = 2024; switch (year) { case 2023 -> { - System.out.println("The Chiefs"); + IO.println("The Chiefs"); } case 2024 -> { - System.out.println("The Chiefs"); + IO.println("The Chiefs"); } default -> { - System.out.println("I don't know"); + IO.println("I don't know"); } } ~} diff --git a/src/switch/null.md b/src/switch/null.md index 5b6d77d..3579b4e 100644 --- a/src/switch/null.md +++ b/src/switch/null.md @@ -6,13 +6,13 @@ When a switch is given a `null` value a `NullPointerException` will be thrown im void eat(String food) { switch (food) { case "dog food" -> { - System.out.println("Crunch"); + IO.println("Crunch"); } case "cat food" -> { - System.out.println("Slorp"); + IO.println("Slorp"); } default -> { - System.out.println("Other food"); + IO.println("Other food"); } } } @@ -29,16 +29,16 @@ case labels. `default` will not suffice. void eat(String food) { switch (food) { case "dog food" -> { - System.out.println("Crunch"); + IO.println("Crunch"); } case "cat food" -> { - System.out.println("Slorp"); + IO.println("Slorp"); } case null -> { - System.out.println("No food"); + IO.println("No food"); } default -> { - System.out.println("Other food"); + IO.println("Other food"); } } } @@ -54,13 +54,13 @@ A default branch and a null case can be combined by separating them with a comma void eat(String food) { switch (food) { case "dog food" -> { - System.out.println("Crunch"); + IO.println("Crunch"); } case "cat food" -> { - System.out.println("Slorp"); + IO.println("Slorp"); } case null, default -> { - System.out.println("Other food"); + IO.println("Other food"); } } } diff --git a/src/switch/omitted_default.md b/src/switch/omitted_default.md index 17ff0aa..4c85015 100644 --- a/src/switch/omitted_default.md +++ b/src/switch/omitted_default.md @@ -7,13 +7,13 @@ If you have no logic to put in it, you can omit the `default` case from a `switc void react(String fruit) { switch (fruit) { case "apple" -> { - System.out.println("WOW"); + IO.println("WOW"); } case "orange" -> { - System.out.println("Zoinks!"); + IO.println("Zoinks!"); } case "grape" -> { - System.out.println("Zoopers!"); + IO.println("Zoopers!"); } } } diff --git a/src/switch/strings.md b/src/switch/strings.md index f64ae4c..7aed3c8 100644 --- a/src/switch/strings.md +++ b/src/switch/strings.md @@ -7,16 +7,16 @@ As already shown, you can use a case to match `String` values. String veggie = "cucumber"; switch (veggie) { case "cabbage" -> { - System.out.println("A cabbage"); + IO.println("A cabbage"); } case "brussel sprout" -> { - System.out.println("A brussel sprout"); + IO.println("A brussel sprout"); } case "cucumber" -> { - System.out.println("A cucumber"); + IO.println("A cucumber"); } default -> { - System.out.println("Other"); + IO.println("Other"); } } ~} diff --git a/src/switch_ii.md b/src/switch_ii.md index d80dac4..78acc88 100644 --- a/src/switch_ii.md +++ b/src/switch_ii.md @@ -32,7 +32,7 @@ void main() { } } - System.out.println(action); + IO.println(action); } ``` diff --git a/src/switch_ii/exhaustiveness.md b/src/switch_ii/exhaustiveness.md index 98c7409..cef3c88 100644 --- a/src/switch_ii/exhaustiveness.md +++ b/src/switch_ii/exhaustiveness.md @@ -11,7 +11,7 @@ boolean cool = switch (name) { default -> true; }; -System.out.println(cool); +IO.println(cool); ~} ``` @@ -25,6 +25,6 @@ boolean cool = switch (name) { case "bob" -> false; }; -System.out.println(cool); +IO.println(cool); ~} ``` diff --git a/src/switch_ii/omitted_yield.md b/src/switch_ii/omitted_yield.md index bec23b9..803abf6 100644 --- a/src/switch_ii/omitted_yield.md +++ b/src/switch_ii/omitted_yield.md @@ -25,7 +25,7 @@ Action action = switch (light) { case GREEN -> Action.GO; }; -System.out.println(action); +IO.println(action); ~} ``` @@ -50,12 +50,12 @@ void main() { Action action = switch (light) { case RED -> Action.STOP; case YELLOW -> { - System.out.println("Lemon Light!"); + IO.println("Lemon Light!"); yield Action.SLOW_DOWN; } case GREEN -> Action.GO; }; - System.out.println(action); + IO.println(action); } ``` \ No newline at end of file diff --git a/src/switch_ii/yield.md b/src/switch_ii/yield.md index 0d05ad6..ad263a2 100644 --- a/src/switch_ii/yield.md +++ b/src/switch_ii/yield.md @@ -30,7 +30,7 @@ void main() { } }; - System.out.println(action); + IO.println(action); } ``` diff --git a/src/switch_iii.md b/src/switch_iii.md index 80eca3f..bf2ce22 100644 --- a/src/switch_iii.md +++ b/src/switch_iii.md @@ -8,24 +8,25 @@ But, due to this history, there is another kind of switch that doesn't use arrow and instead uses a colon (`:`). ```java -boolean shouldBeMainCharacter(String name) { - switch (name) { - case "Gohan": - return true; - case "Goku": - default: - return false; +class Main { + boolean shouldBeMainCharacter(String name) { + switch (name) { + case "Gohan": + return true; + case "Goku": + default: + return false; + } + } + + void main() { + IO.println( + shouldBeMainCharacter("Goku") + ); } -} -void main() { - System.out.println( - shouldBeMainCharacter("Goku") - ); } ``` This "C-Style Switch" is important to learn chiefly because, for a long time, it was the only switch in Java. Therefore in your coding life you are very likely to -run into it when reading old code. - -It also might be uniquely useful for you once in a blue moon. Who knows. \ No newline at end of file +run into it. \ No newline at end of file diff --git a/src/switch_iii/break.md b/src/switch_iii/break.md index c95437e..b1d88e6 100644 --- a/src/switch_iii/break.md +++ b/src/switch_iii/break.md @@ -1 +1,78 @@ # break + +After each case label in a C-style switch, you should write `break;`.[^cookout] + +```java +class Main { + void main() { + String name = "Piccolo"; + switch (name) { + case "Squidward": + IO.println("Invited, but not coming."); + break; + + case "Piccolo": + IO.println("Coming to the cookout."); + break; + + case "Spider-Man": + IO.println("Not coming"); + break; + } + } +} +``` + +This is different from breaking out of a loop and won't break out of any surrounding loops. + +```java +class Main { + void main() { + for (int i = 0; i < 3; i++) { + // Will still run 3 times + String name = "Piccolo"; + switch (name) { + case "Squidward": + IO.println("Invited, but not coming."); + break; + + case "Piccolo": + IO.println("Coming to the cookout."); + break; + + case "Spider-Man": + IO.println("Not coming"); + break; + } + } + } +} +``` + +To break out of surrounding loops you can use a labeled break. + +```java +class Main { + void main() { + outerLoop: + for (int i = 0; i < 3; i++) { + String name = "Piccolo"; + switch (name) { + case "Squidward": + IO.println("Invited, but not coming."); + break; + + case "Piccolo": + IO.println("Coming to the cookout."); + break outerLoop; // Will break out of the loop + + case "Spider-Man": + IO.println("Not coming"); + break; + } + } + } +} +``` + +[^cookout]: [Who should be invited to the cookout?](https://www.youtube.com/watch?v=64SoFWJHSd8) \ No newline at end of file diff --git a/src/switch_iii/default.md b/src/switch_iii/default.md index 36755d2..5747eb7 100644 --- a/src/switch_iii/default.md +++ b/src/switch_iii/default.md @@ -1 +1,65 @@ # default + +Just like with normal switches, C-style switches can have a `default` label +that matches when no other label does. + +```java +class Main { + boolean shouldBeMainCharacter(String name) { + switch (name) { + case "Gohan": + return true; + default: + return false; + } + } + + void main() { + IO.println( + shouldBeMainCharacter("Goku") + ); + } +} +``` + +If you have a C-style switch over an enum you need this `default` case for exhaustiveness. Java does not +otherwise accept that you have covered all the cases. + +```java,does_not_compile +enum Technique { + KAMEHAMEHA, + INSTANT_TRANSMISSION, + KAIOKEN, + ULTRA_INSTINCT +} + +class Main { + boolean didGokuStealItFromSomeoneElse(Technique technique) { + switch (technique) { + case KAMEHAMEHA: + IO.println("Master Roshi Taught it to him"); + return true; + case INSTANT_TRANSMISSION: + IO.println("Space aliens"); + return true; + case KAIOKEN: + IO.println("King Kai's name is in it!"); + return true; + case ULTRA_INSTINCT: + IO.println("I'd say not"); + return false; + // Even though we covered every option, Java doesn't trust us. + // You need a default label or to return later in the function + // + // default: + // throw new IllegalStateException("Unexpected: " + technique); + } + } + + void main() { + IO.println( + didGokuStealItFromSomeoneElse(Technique.INSTANT_TRANSMISSION) + ); + } +} +``` \ No newline at end of file diff --git a/src/switch_iii/fallthrough.md b/src/switch_iii/fallthrough.md index c3c2261..fb2dbb7 100644 --- a/src/switch_iii/fallthrough.md +++ b/src/switch_iii/fallthrough.md @@ -1,4 +1,31 @@ # Fallthrough -If the code for a given label does not `break` -or `return` then \ No newline at end of file +If the code for a given label does not have a `break` then it will "fall through" +to the cases below. + +This is what makes C-style switches strange. It can occasionaly be useful if the same code should +run for some or all cases, but is annoyingly easy to do on accident.[^history] + +```java +class Main { + void sayWhoTheyFought(String name) { + switch (name) { + case "Goku": + IO.println("Fought Pilaf"); + IO.println("Fought The Red Ribbon Army"); + case "Gohan": // "Goku" will fall through to this case + IO.println("Fought Frieza"); + IO.println("Fought Cell"); + IO.println("Fought Majin Buu"); + } + } + + void main() { + sayWhoTheyFought("Gohan"); + IO.println("----------------------"); + sayWhoTheyFought("Goku"); + } +} +``` + +[^history]: [This StackExchange Post](https://softwareengineering.stackexchange.com/questions/162615/why-dont-languages-use-explicit-fall-through-on-switch-statements) explains how this came about. I don't have a primary source on the "The reason that C did it that way is that the creators of C intended switch statements to be easy to optimize into a jump table." claim, but it lines up with my biases and preconceptions. Therefore it must be true! \ No newline at end of file diff --git a/src/switch_iii/return.md b/src/switch_iii/return.md index bc7f48b..a33f223 100644 --- a/src/switch_iii/return.md +++ b/src/switch_iii/return.md @@ -1 +1,30 @@ # return + +If you explicitly `return` from inside a C-style switch then there is no need to have a `break` +to avoid fallthrough. + +```java +class Main { + void sayWhoTheyFought(String name) { + switch (name) { + case "Launch": + IO.println("Fought Red Ribbon Army"); + IO.println("Fought 3 nameless convicts"); + return; // This will return from the whole method + case "Goku": + IO.println("Fought Pilaf"); + IO.println("Fought The Red Ribbon Army"); + case "Gohan": + IO.println("Fought Frieza"); + IO.println("Fought Cell"); + IO.println("Fought Majin Buu"); + } + } + + void main() { + sayWhoTheyFought("Launch"); + } +} +``` + +This should be intuitive. diff --git a/src/switch_iii/yield.md b/src/switch_iii/yield.md index 20c1740..5003c9f 100644 --- a/src/switch_iii/yield.md +++ b/src/switch_iii/yield.md @@ -1 +1,36 @@ # yield + +If every branch of a C-style switch `yield`s a value, you can use that switch as an expression. + +```java +enum Technique { + KAMEHAMEHA, + INSTANT_TRANSMISSION, + KAIOKEN, + ULTRA_INSTINCT +} + +class Main { + boolean didGokuStealItFromSomeoneElse(Technique technique) { + boolean answer = switch (technique) { + case KAMEHAMEHA: + case INSTANT_TRANSMISSION: + case KAIOKEN: + yield true; + case ULTRA_INSTINCT: + yield false; + }; + + return answer; + } + + void main() { + IO.println( + didGokuStealItFromSomeoneElse(Technique.INSTANT_TRANSMISSION) + ); + } +} +``` + +In this situation you do not need to have a `default` case for switches over enums. Java will believe that +you covered every possibility. \ No newline at end of file diff --git a/src/tcp b/src/tcp deleted file mode 100644 index 94dd956..0000000 --- a/src/tcp +++ /dev/null @@ -1 +0,0 @@ -# Low Level versus High Level diff --git a/src/the_terminal/run_java_programs.md b/src/the_terminal/run_java_programs.md index 2259cf6..6531ede 100644 --- a/src/the_terminal/run_java_programs.md +++ b/src/the_terminal/run_java_programs.md @@ -9,7 +9,7 @@ Say we had this code in a file named `Main.java` ```java void main() { - System.out.println("Hello"); + IO.println("Hello"); } ``` diff --git a/src/time/challenges.md b/src/time/challenges.md new file mode 100644 index 0000000..9358534 --- /dev/null +++ b/src/time/challenges.md @@ -0,0 +1 @@ +# Challenges diff --git a/src/time/date.md b/src/time/date.md index eef5ce9..6da42bc 100644 --- a/src/time/date.md +++ b/src/time/date.md @@ -1 +1,59 @@ # Date + +There is one class related to time that isn't much like the others: `java.util.Date`. + +Java did not originally come with the `java.time` classes. At first it just had `Date`. + +```java +import java.util.Date; + +class Main { + void main() { + Date date = new Date(); + IO.println(date); + } +} +``` + +`Date` is somewhat of a chimera between `Instant` and `ZonedDateTime`. It represents an instant in time but specifically in the UTC timezone.[^gmt] + +It is important to know about because a lot of code, including code that comes with Java, makes use of it. + +Whenever you see `Date` in the wild you should usually turn it into an `Instant` by +calling `.toInstant()`. + +```java +import java.time.Instant; +import java.util.Date; + +class Main { + void main() { + Date date = new Date(); + IO.println(date); + + Instant instant = date.toInstant(); + IO.println(instant); + } +} +``` + +You can also construct a `Date` from an `Instant` using `Date.from`. This is useful if there is some code that wants a `Date` as an argument. + +```java +import java.time.Instant; +import java.util.Date; + +class Main { + void main() { + var instant = Instant.now(); + IO.println(instant); + + Date date = Date.from(instant); + IO.println(date); + } +} +``` + +To be clear though, `Date` has problems. We aren't ready to explain all of them yet. Just treat `Date` as haunted, as in by ghosts, and use the `java.time` alternatives when you can. + +[^gmt]: You will notice that when we print out the date we get GMT. GMT is basically the same as UTC, though [the documentation for `Date`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Date.html) explains the difference. \ No newline at end of file diff --git a/src/time/duration.md b/src/time/duration.md index c9e7eb9..7150595 100644 --- a/src/time/duration.md +++ b/src/time/duration.md @@ -10,10 +10,10 @@ import java.time.Duration; void main() { var fiveMinutes = Duration.ofMinutes(5); - System.out.println(fiveMinutes); + IO.println(fiveMinutes); var twelveMilliSeconds = Duration.ofMillis(12); - System.out.println(twelveMilliSeconds); + IO.println(twelveMilliSeconds); } ``` @@ -26,13 +26,13 @@ import java.time.Duration; void main() { var january2nd = Instant.ofEpochMilli(86400000); - System.out.println(january2nd); + IO.println(january2nd); var january3rd = Instant.ofEpochMilli(86400000 * 2); - System.out.println(january3rd); + IO.println(january3rd); Duration twentyFourHours = Duration.between(january2nd, january3rd); - System.out.println(twentyFourHours); + IO.println(twentyFourHours); } ``` @@ -45,13 +45,13 @@ import java.time.Duration; void main() { var january1st = Instant.ofEpochMilli(0); - System.out.println(january1st); + IO.println(january1st); - System.out.println( + IO.println( january1st.plus(Duration.ofHours(45)) ); - System.out.println( + IO.println( january1st.minus(Duration.ofHours(1)) ); } diff --git a/src/time/instant.md b/src/time/instant.md index 6421297..97e5cac 100644 --- a/src/time/instant.md +++ b/src/time/instant.md @@ -9,7 +9,7 @@ import java.time.Instant; void main() { var now = Instant.now(); - System.out.println(now); + IO.println(now); } ``` @@ -21,7 +21,7 @@ import java.time.Instant; void main() { var january2nd = Instant.ofEpochMilli(86400000); - System.out.println(january2nd); + IO.println(january2nd); } ``` diff --git a/src/time/local_date.md b/src/time/local_date.md index 5d9438a..64e7ea7 100644 --- a/src/time/local_date.md +++ b/src/time/local_date.md @@ -12,8 +12,8 @@ You can make a `LocalDate` with `LocalDate.of`. import java.time.LocalDate; void main() { - var jan10 = LocalDate.of(2024, 10, 1); - System.out.println(jan10); + var jan10 = LocalDate.of(2024, 1, 10); + IO.println(jan10); } ``` @@ -25,6 +25,6 @@ import java.time.LocalDate; void main() { var now = LocalDate.now(); - System.out.println(now); + IO.println(now); } ``` \ No newline at end of file diff --git a/src/time/local_date_time.md b/src/time/local_date_time.md index 2c5e886..c7c124d 100644 --- a/src/time/local_date_time.md +++ b/src/time/local_date_time.md @@ -14,9 +14,9 @@ import java.time.LocalDateTime; class Main { void main() { - var jan10 = LocalDate.of(2024, 10, 1); + var jan10 = LocalDate.of(2024, 1, 10); var tenTwentyFour = LocalTime.of(10, 24, 0); - System.out.println(LocalDateTime.of(jan10, tenTwentyFour)); + IO.println(LocalDateTime.of(jan10, tenTwentyFour)); } } ``` \ No newline at end of file diff --git a/src/time/local_time.md b/src/time/local_time.md index 2b2d2e6..7e475f7 100644 --- a/src/time/local_time.md +++ b/src/time/local_time.md @@ -13,7 +13,7 @@ import java.time.LocalTime; void main() { var tenTwentyFour = LocalTime.of(10, 24, 0); - System.out.println(tenTwentyFour); + IO.println(tenTwentyFour); } ``` @@ -24,6 +24,6 @@ import java.time.LocalTime; void main() { var now = LocalTime.now(); - System.out.println(now); + IO.println(now); } ``` \ No newline at end of file diff --git a/src/time/offset_date_time.md b/src/time/offset_date_time.md index b6afe8e..5c7a362 100644 --- a/src/time/offset_date_time.md +++ b/src/time/offset_date_time.md @@ -1 +1,63 @@ # OffsetDateTime + +An `OffsetDateTime` is similar to a `ZonedDateTime` but with the key difference +that an `OffsetDateTime` doesn't record a moment in a specific time zone, but instead +as an offset from the [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time)[^utc] timezone. + +This is useful because timezones change their rules frequently. If you had to pick a representation +of dates and times to store, this is a good default. + +```java +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; + +class Main { + void main() { + var feb14 = LocalDate.of(2025, 2, 14); + var fiveTwentyThree = LocalTime.of(5, 23, 0); + var est = ZoneId.of("US/Eastern"); + + LocalDateTime localDateTime = LocalDateTime.of(feb14, fiveTwentyThree); + ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, est); + OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime(); + + IO.println(offsetDateTime); + } +} +``` + +You can get the current `OffsetDateTime` for the time zone your computer is running in +with `OffsetDateTime.now()`. + +```java +import java.time.OffsetDateTime; + +class Main { + void main() { + var now = OffsetDateTime.now(); + + IO.println(now); + } +} +``` + +And you can do the same for an arbitrary time zone by giving a `ZoneId` to +`now`. Java knows the UTC offset for every time zone. + +```java +import java.time.OffsetDateTime; +import java.time.ZoneId; + +class Main { + void main() { + var now = OffsetDateTime.now(ZoneId.of("US/Eastern")); + + IO.println(now); + } +} +``` + +[^utc]: UTC stands for "Coordinated Universal Time." No I don't know why the letters are in a different order [and at this point I'm too afraid to ask](https://knowyourmeme.com/memes/afraid-to-ask-andy). \ No newline at end of file diff --git a/src/time/time_zones.md b/src/time/time_zones.md index db59bef..f81d1a8 100644 --- a/src/time/time_zones.md +++ b/src/time/time_zones.md @@ -19,7 +19,7 @@ class Main { void main() { ZoneId tz = ZoneId.systemDefault(); - System.out.println(tz); + IO.println(tz); } } ``` @@ -38,7 +38,7 @@ class Main { // Eastern Standard Time ZoneId tz = ZoneId.of("US/Eastern"); - System.out.println(tz); + IO.println(tz); } } ``` @@ -55,7 +55,7 @@ class Main { // Eastern Standard Time ZoneId tz = ZoneId.of("US/Eastern"); - System.out.println(tz.getRules()); + IO.println(tz.getRules()); } } ``` diff --git a/src/time/zoned_date_time.md b/src/time/zoned_date_time.md index 58d5836..80149bc 100644 --- a/src/time/zoned_date_time.md +++ b/src/time/zoned_date_time.md @@ -15,14 +15,14 @@ import java.time.ZoneId; class Main { void main() { - var jan10 = LocalDate.of(2024, 10, 1); + var jan10 = LocalDate.of(2024, 1, 10); var tenTwentyFour = LocalTime.of(10, 24, 0); var est = ZoneId.of("US/Eastern"); LocalDateTime localDateTime = LocalDateTime.of(jan10, tenTwentyFour); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, est); - System.out.println(zonedDateTime); + IO.println(zonedDateTime); } } ``` @@ -37,7 +37,7 @@ class Main { void main() { var now = ZonedDateTime.now(); - System.out.println(now); + IO.println(now); } } ``` @@ -53,7 +53,7 @@ class Main { void main() { var now = ZonedDateTime.now(ZoneId.of("US/Eastern")); - System.out.println(now); + IO.println(now); } } ``` diff --git a/src/variables.md b/src/variables.md index 5c9294e..5a0a148 100644 --- a/src/variables.md +++ b/src/variables.md @@ -6,7 +6,7 @@ Mechanically, the next thing to cover is "variables". ```java void main() { String boss = "Jaqueline"; - System.out.println(boss); + IO.println(boss); } ``` @@ -25,8 +25,8 @@ hand side and you can use that name instead of the value. ```java ~void main() { -// Does the same thing as System.out.println("Jaqueline"); +// Does the same thing as IO.println("Jaqueline"); String boss = "Jaqueline"; -System.out.println(boss); +IO.println(boss); ~} ``` diff --git a/src/variables/challenges.md b/src/variables/challenges.md index 9fcbc0e..79f00f4 100644 --- a/src/variables/challenges.md +++ b/src/variables/challenges.md @@ -12,11 +12,11 @@ What will this program output when run? Write down your guess and then try runni ```java,editable void main() { String mascot = "The Noid"; - System.out.println(mascot); + IO.println(mascot); mascot = "Pizza the Hut"; - System.out.println(mascot); + IO.println(mascot); mascot = "Little Caesar"; - System.out.println(mascot); + IO.println(mascot); } ``` @@ -29,18 +29,18 @@ void main() { String fruit; fruit = "apple"; - System.out.println(fruit); + IO.println(fruit); final String vegetable = "carrot"; - System.out.println(fruit); - System.out.println(vegetable); + IO.println(fruit); + IO.println(vegetable); fruit = "orange"; vegetable = "celery"; - System.out.println(fruit); - System.out.println(vegetable); + IO.println(fruit); + IO.println(vegetable); } ``` @@ -58,8 +58,8 @@ void main() { b = a; a = b; - System.out.println(a); - System.out.println(b); + IO.println(a); + IO.println(b); } ``` @@ -82,8 +82,8 @@ void main() { // You can add code here // Don't touch below this - System.out.println(a); - System.out.println(b); + IO.println(a); + IO.println(b); } ``` diff --git a/src/variables/delayed_assignment.md b/src/variables/delayed_assignment.md index 1a548b2..c701fb8 100644 --- a/src/variables/delayed_assignment.md +++ b/src/variables/delayed_assignment.md @@ -8,7 +8,7 @@ void main() { String contestWinner; contestWinner = "Boaty McBoatface"; - System.out.println(contestWinner); + IO.println(contestWinner); } ``` @@ -33,7 +33,7 @@ void main() { String contestWinner; // This will not run, since Java knows that // you never gave contestWinner an initial value. - System.out.println(contestWinner); + IO.println(contestWinner); } ``` diff --git a/src/variables/final_variables.md b/src/variables/final_variables.md index f8dd78a..057fbef 100644 --- a/src/variables/final_variables.md +++ b/src/variables/final_variables.md @@ -6,7 +6,7 @@ mark a variable as "final", meaning its value can never be reassigned. ```java ~void main() { final String coolestChef = "Anthony Bourdain"; -System.out.println(coolestChef); +IO.println(coolestChef); ~} ``` @@ -16,11 +16,11 @@ If you try to reassign a final variable, Java will not accept your program. ```java,does_not_compile ~void main() { final String coolestChef = "Anthony Bourdain"; -System.out.println(coolestChef); +IO.println(coolestChef); // I'm sorry, but no. Cool guy, but no. coolestChef = "Gordan Ramsey"; -System.out.println(coolestChef); +IO.println(coolestChef); ~} ``` @@ -41,7 +41,7 @@ final String genie = "Robin Williams"; // ...... // You can still be sure "genie" // has the value of "Robin Williams" -System.out.println(genie); +IO.println(genie); ``` Variables whose assignment is delayed can also be marked final. @@ -50,7 +50,7 @@ Variables whose assignment is delayed can also be marked final. ~void main() { final String mario; mario = "Charles Martinet"; -System.out.println(mario); +IO.println(mario); ~} ``` @@ -65,7 +65,7 @@ mario = "Charles Martinet"; // But you cannot reassign it afterwards mario = "Chris Pratt"; -System.out.println(mario); +IO.println(mario); ~} ``` diff --git a/src/variables/inferred_types.md b/src/variables/inferred_types.md index df19bc2..214f33b 100644 --- a/src/variables/inferred_types.md +++ b/src/variables/inferred_types.md @@ -11,7 +11,7 @@ be. // of the = is in quotes, Java knows that // it is a String. var theDude = "Lebowski"; -System.out.println(theDude); +IO.println(theDude); ~} ``` @@ -23,7 +23,7 @@ You cannot use `var` with variables whose assignment is delayed. // Java doesn't know enough to infer the type var theDude; theDude = "Lebowski"; -System.out.println(theDude); +IO.println(theDude); ~} ``` @@ -33,7 +33,7 @@ and cannot be reassigned. ```java ~void main() { final var theDude = "Lebowski"; -System.out.println(theDude); +IO.println(theDude); ~} ``` @@ -43,6 +43,6 @@ you might not be yet. There is no shame in writing out the type explicitly. ```java ~void main() { String theDude = "lebowski"; -System.out.println(theDude); +IO.println(theDude); ~} ``` diff --git a/src/variables/reassignment.md b/src/variables/reassignment.md index 77a0451..639b761 100644 --- a/src/variables/reassignment.md +++ b/src/variables/reassignment.md @@ -5,9 +5,9 @@ After a variable is declared and assigned an initial value, that value can be la ```java void main() { String boss = "Jaqueline"; - System.out.println(boss); + IO.println(boss); boss = "Chelsea"; - System.out.println(boss); + IO.println(boss); } ``` @@ -26,9 +26,9 @@ the new value from that point in the program onwards. void main() { String boss = "Jaqueline"; // This will output "Jaqueline" - System.out.println(boss); + IO.println(boss); boss = "Chelsea"; // But this will output "Chelsea" - System.out.println(boss); + IO.println(boss); } ``` diff --git a/src/visibility.md b/src/visibility.md index 8ef10a4..91a4f34 100644 --- a/src/visibility.md +++ b/src/visibility.md @@ -6,7 +6,7 @@ if there is a method you are always allowed to call it. ```java class Main { void canCallThis() { - System.out.println("of course!") + IO.println("of course!") } void main() { diff --git a/src/visibility/accessors.md b/src/visibility/accessors.md index 3de25cb..2c895a5 100644 --- a/src/visibility/accessors.md +++ b/src/visibility/accessors.md @@ -28,7 +28,7 @@ class Main { // dog.name won't work because the name field is private // dog.name() will work because the name method is not - System.out.println(dog.name()); + IO.println(dog.name()); } } ~class Dog { @@ -49,7 +49,7 @@ We would also consider things like the `length` method on `String`s to be "acces ```java void main() { String s = "abc"; - System.out.println( + IO.println( // We can't see what fields underly this, // but we can access the length s.length()