Nailing 1Z0-808 Free Sample
Nailing 1Z0-808 Free Sample
to
Oracle Java SE8 Programmer I Certification
by Igor Soudakevitch
FREE SAMPLE
Contains full text of Chapter II.9:
Exam Objectives – Group 9
(Working with Selected Classes from Java API)
In file C:\Try_Java\tempa\Exam.java:
1 package tempa;
2 import tempb.*;
3
4 public class Exam {
5 protected Exam(Examinee e){}
6 }
In file C:\Try_Java\tempb\Examinee.java:
1 package tempb;
2 import tempa.Exam;
3
4 public class Examinee {
5 String str;
6 protected Examinee(){
7 this.str = "1Z0-808 is tricky!";
8 }
9 protected void exclaim(){
10 System.out.println(str);
11 }
12 }
In file C:\Try_Java\org\xlator\TargetAudience.java:
1 package org.xlator;
2 import tempa.*;
3 import tempb.Examinee;
4
5 public final class TargetAudience extends Examinee {
6 String str = "Then again, maybe not...";
7 TargetAudience(){
8 exclaim();
9 }
10 @Override
11 public void exclaim(){
12 System.out.println(str.substring(0, str.length()));
13 }
14 public static void main(String[] args) {
15 Examinee ta = new TargetAudience();
16 new Exam(ta);
17 }
18 }
Target Audience 3
Answer to the ‛Target Audience’ question:
The correct answer is option C for the code does not compile.
The fail-safe way to solve problems on the exam is to read question first, then glance at the
available choices to see if some of them can be eliminated immediately because options are
syntactically invalid quite often, and only after that start analyzing code. Let me do it for you:
The entire chain of reasoning takes but twenty seconds, and this is what you too will be able to
pull off every single time on the exam regardless of the problem you’re facing – after you
worked your way through this OCA bootcamp with all its obstacle courses, that is.
This book has one sole purpose: to hone your skills for solving exam questions both reliably and
quickly; as such, it doesn’t even attempt to replace a full-fledged Java study guide. Ideally,
Nailing 1Z0-808 should become the beachhead from which you’ll be launching your final
offensive to conquer the coveted ‛Oracle Certified Programmer I’ title. This entire tune-up,
refueling and ammo-stocking phase won’t take more than three weeks. However, to benefit from
it fully you should already know your way around Java basics so grab a solid tutorial, study
online 1 or watch video lessons and so on; Preface lists some reliable titles.
★★★★★
I’d be very much interested in hearing how you fared on the exam. Do find a minute to tell me
your score and what objectives you missed. Comments or suggestions are welcome.
www.igor.host
1
You can find free Java online courses at MIT, UC Berkeley, etc. Stay away from YouTube, though: it has no QC.
9.1 Manipulate data using the StringBuilder class and its methods +
9.2 Creating and manipulating Strings:
Both String and StringBuilder implement the interface CharSequence and use a char
array to store their characters but, unlike StringBuilder objects, String objects are
immutable.
Most important corollary: None of the methods defined in the class String can
modify original String object’s value.
(other wording) If a String method returns a modified String, this object is new.
In practice, Strings can be created in two ways: by forming a string literal (a sequence of
characters enclosed by double quotes) or by using the keyword new.
In Java, a string literal is itself an object, which gets interned in a common string pool 2
(as per JLS, §3.10.5, String Literals: "A string literal is a reference to an instance of class
String. Moreover, a string literal always refers to the same instance of class String").
Whenever the code requests creation of a string literal, the pool is scanned and, if a
String object with the same value already exists in the pool, the reference to this object is
returned. String objects created using the keyword new are never placed in the pool of
String objects; same goes for the Strings that are products of methods and, therefore,
computed at run time:
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = new String(new StringBuilder("Hello"));
2
Do not confuse "string pool" with "constant pool", which is allocated at run time and "…contains several kinds of
constants, ranging from numeric literals known at compile-time to method and field references that must be resolved
at run-time" (JVM Spec, Java SE8 Ed., §2.5.5, Run-Time Constant Pool).
EXTRA: String pool is common for all the classes from all the packages that are being
used by the running application.
String constructors likely to appear on the exam (italics: didn’t meet personally):
String(String str)
String(StringBuilder sb)
EXTRA: Although a no-arg constructor String() does exist, it isn’t used in practice
because String objects are immutable in the first place.
StringBuilder constructors likely to appear on the exam (italics: didn’t meet):
StringBuilder(String str)
StringBuilder() ← Constructs an empty sb with initial capacity of 16 characters.
StringBuilder(int capacity)
String and StringBuilder methods likely to appear on the exam:
Method args str sb Note
substring() (int start) or (int start, int end) NB: returns String rather than SB
length() ()
str: (CS trgt, CS replcmnt) or (oldCh, newCh) NB: diff params for str and sb
replace()
sb: (int start, int end, String str) no change? → current object
equals() (Object obj) SB doesn’t override equals()
concat() (String str) no change? → current object
toLowerCase() () no change? → current object
toUpperCase() () no change? → current object
trim() () no change? → current object
equalsIgnoreCase() (String anotherString)
append() (all kinds of types including String)
insert() (int offset, all kinds of types including String)
delete() (int start, int end)
REMARK: The table is not exhaustive; it only means that I personally met those
methods on the exam. To be on the safe side, learn the following
methods, as well:
– concat(), endsWith, startsWith() String only
– reverse(), capacity(), ensureCapacity() StringBuilder only
– charAt(), indexOf() String & StringBuilder
– since == tests for ‛physical’ equality, the equality of contents should be tested with
the method equals();
– whereas String and ArrayList override Object’s equals(), StringBuilder does
not → use a workaround: sb1.toString().equals(sb2.toString());
Easily confused methods (list refers to ArrayList):
Method str sb list Note
insert() inserts at any position up to length(); returns current SB
append() attaches to the tail; returns current StringBuilder
add() returns boolean except void add(int index, E element)
replace() str: replaces each occurrence; sb: replaces a substring
replaceAll() String uses regexing; List replaces a Collection
set() returns the element previously at the specified position
setCharAt() void
delete() returns current SB; if start == end → no changes ≡ same object
deleteCharAt() returns StringBuilder
clear() void; removes all the elements
remove() returns the element previously at the specified position
removeAll() returns true if the list changed as a result of the call
charAt() returns the char value at the specified index
get() returns the element at the specified position
contains() true if obj contains specified CharSeq / or Object (List)
isEmpty() true if String’s length is 0 / if List contains no elements
size() returns the # of elements (stops at Integer.MAX_VALUE)
length() returns the length of this String / char count in case of SB
setLength() appends null(s) ‛\u0000’ if newLength > oldLength
ensureCapacity() void
trim() removes leading and trailing whitespace; same obj if no change
trimToSize() resizes the buffer if it is larger than currently necessary
reverse() NB: surrogate pairs are treated as single characters
Other possible traps:
– str.trim() doesn’t remove whitespace 4 inside the string;
– sb.substring() returns a newly created String without affecting the current sb
object whereas sb.append(), sb.delete() and sb.insert() do change it;
– str[sb].substring() throws SIOOBE if the end arg exceeds str[sb].length():
System.out.println(new StringBuilder("Hi").substring(1,3)); // SIOOBE
3
Means either str.substring() or sb.substring().
4
space (0x20), horizontal tab (0x09), form feed (0x0C) and line terminators LF (0x0A), CR (0x0D) and CRLF.
a = 1 + 10 + a; // 11
The principal operations on a StringBuilder object are the append() and insert()
methods, which are overloaded so as to accept data of any type. Each effectively converts
a given datum to a string.
Corollaries: append()ing and insert()ing a null object won't throw an NPE.
EXTRA: When a null object (such as String, etc.) is passed in as a parameter to these
two methods, it will be converted to the four characters ‛null’.
EXTRA: However, append(null) and insert(null) won’t compile as the reference is
ambiguous: null can refer to either char[] or CharSequence
Useful reminders:
■ charAt(), indexOf(), substring(), and length() work in the same way both in String
and StringBuilder.
■ append() adds only to the end of the current StringBuilder object whereas insert()
allows to put in one or multiple characters at a specified position
→ sb.insert(sb.length(),"***") behaves as sb.append("***");
■ StringBuilder uses a char array and throws a SIOOBE rather than AIOOBE.
■ StringBuilder doesn’t have remove(); it’s delete().
Problem 9.5
Hoping to land a job as a junior programmer at a local bank, you came to a Java interview and
were asked to complete the following class that masks 20-digit account numbers so that only the
last five digits will be visible to the user while each of the first 15 digits is replaced with an
asterisk:
public class AccountMasker {
public static String maskAccount(String accNum) {
String lead = "***************"; // contains 15 asterisks in a row
Specifically, your task is to finish up the method maskAccount(String accNum) so that the class
AccountMasker will output ****************67541 when run with the following command:
java AccountMasker "34296 01853 49820 67541"
Which two LOCs, when used independently, will achieve this requirement?
A. return (new StringBuilder(accNum)).substring(15, 20).toString();
B. return lead + accNum.substring(15, 20);
C. return new StringBuilder(accNum).append(lead, 15, 20).toString();
D. return new StringBuilder(accNum).insert(0, lead).toString();
E. return new StringBuilder(lead).append(accNum, 15, 20).toString();
How many LOCs, when inserted independently at line XXX, will make the code print
Strings Forever!?
A. None
B. One
C. Two
D. Three
Is it true that the code prints String Quartet No. 19 in C Major, K. 465?
A. true
B. false
9.3 Create and manipulate calendar data using classes from java.time.LocalDateTime,
java.time.LocalDate, java.time.LocalTime, java.time.format.DateTimeFormatter,
java.time.Period
All of the LDT-related classes are immutable.
None of the LDT-related classes (incl. DTF and Period) have public constructors.
Corollary: All three primary classes are instantiated with now() or of(), e.g.:
LocalTime.now(); // 09:10:11.123 @ 9:10:11.123 a.m.
LocalDateTime.now(); // 2016-06-13T09:10:18.429
LocalDate.of(2016, Month.JUNE, 13); // 2016-06-13
LocalDateTime.of(2016,6,13,23,59); // 2016-06-13T23:59
Known trap on the exam: Since the classes are immutable, adding or subtracting
time units becomes meaningless unless the code uses
the values returned by these methods (similarly to the
methods invoked on String objects).
LocalDate ld = LocalDate.of(2016, Month.JUNE, 13);
ld.plusDays(2);
System.out.println(ld); // still 2016-06-13...
NB: Days, months, hours and minutes should be specified with TWO digits,
otherwise a DateTimeParseException is thrown; this rule isn’t applicable to
the method of().
LocalDate ld = LocalDate.parse("2016-6-13"); // throws DTPE
System.out.println(LocalDate.parse("2016-06-13")); // 2016-06-13
LocalTime lt = LocalTime.parse("1:2"); // throws DTPE
System.out.println(LocalDateTime.of(2017,1,1,1,1)); // 2017-01-01T01:01
The class Period represents a number of days, months, weeks, or years, which can
be added to or subtracted from a LocalDate or a LocalDateTime object:
LocalDate someDate = LocalDate.of(2001, Month.JANUARY, 1)
.plus(Period.ofMonths(-3))
.minus(Period.ofYears(5)); // 1995-10-01
5
Because Period’s ofXXX() methods are static; all they do is just return a newly created Period object.
6
Something like ofLocalizedTime(timeStyle) or RFC_1123_DATE_TIME is obviously out of scope for 1Z0-808.
Working with Selected Classe from Java API Chapter II.9 19
EXERCISES (for Key Summary ref.to App.D):
Problem 9.29
Which two LOCs fail compilation?
A. LocalDateTime.of(2016,6,13);
B. LocalDate.of(2016, Month.JUNE, 50);
C. LocalDateTime.of(2016,06,13,14,15);
D. LocalDate ld = new LocalDate(2016,6,13);
E. LocalTime.now().format(DateTimeFormatter.ISO_DATE);
One of the LOCs fails compilation. Which two modifications, used independently, will
make the code print Java was born on May 23, 1995?
A. Replace line 2 with import java.time.*;
B. Replace line 3 with import java.time.format.*;
C. Replace Month.MAY on line 7 with Month.May
D. Replace Month.MAY on line 7 with 05
7
Citing the Arrays API docs: "static <T> List<T> asList(T... a) returns a fixed-size list backed by the specified
array".
8
Being a utility class, Collections is made up entirely of static methods; same goes for java.util.Arrays.
Working with Selected Classe from Java API Chapter II.9 23
EXERCISES (for Key Summary ref.to App.D):
9.5 Write a simple lambda expression that consumes a lambda Predicate expression:
The abstract method test() declared in the interface Predicate returns a boolean
and takes an argument of type Object.
Since there’s only one formal parameter (which corresponds to test()’s arg), its type
and enclosing parentheses are optional.
If the method body contains a single boolean-returning stat, the braces can be
omitted UNLESS the stat uses an explicit return – in which case we must apply
the Semirebra 9 Rule.
The Semirebra Rule:
Semicolon, return and braces in lambda method body always go together.
Traps! – empty parameter list, or
– return without semicolon and braces, or
– semicolon without return and braces, or
– the method body does not return a boolean.
Typical invalid patterns likely to appear on the exam:
( ) -> whatever // param list is empty
(x,y) -> whatever // too many params
(x) -> System.out.print(x) // System.out.print() is void
x -> { return "" + x; } // String instead of a boolean
str -> return str.isEmpty(); // return and ; need {}
str -> { return str.isEmpty() } // return and {} need ;
str -> str.isEmpty(); // ; needs both return and {}
whtvr -> return true // missing both ; and {}
What Predicate is typed to must be consistent with the parameter’s type.
Consider the following:
static String check(List list, Predicate p){
return p.test(list)? "Empty" : "Not empty"; }
public static void main(String[] args) {
ArrayList list = new ArrayList();
System.out.println(check(list, al -> al.isEmpty())); // INVALID
}
The above example fails to compile because p isn’t generified → its type is therefore
Object → and Object doesn’t have the method isEmpty(). On the other hand, the
code would have compiled if al.isEmpty() were replaced, say, with al.equals("").
Better yet, cast the formal param to List or ArrayList (e.g., ((List)al).isEmpty()) as
this wouldn’t break the logic behind the code, which happens with equals("").
Let’s have another example that doesn’t compile:
static String check(List list, Predicate<ArrayList> p){
return p.test(list)? "Empty" : "Not empty"; // INVALID
}
public static void main(String[] args) {
List list = new ArrayList();
System.out.println(check(list, al -> al.isEmpty()));
}
9
Just an acronym: ‛semi’ for ‛semicolon’, ‛re’ for ‛return’, and ‛bra’ for ‛braces’. As the Semirebra Rule is
specifically tailored to java.util.function.Predicate, it is meant to be used on the 1Z0-808 exam only.
Did it help? Nope, the comperr just moved to the printing stat’s line: although test()
is perfectly happy now because it expected a toy of type List while what it’s gonna
get will be even more sophisticated and amusing, the actual call to check() provides
the same unrefined, boring List. If the available options prevent us from touching
the check() method’s signature, we could change list’s reftype:
ArrayList list = new ArrayList();
Another way to deal with this problem involves casting list to ArrayList:
System.out.println(check((ArrayList)list, al -> al.isEmpty()));
After all, the actual type of the object at run time is ArrayList, so this quick’n’dirty
hack will even work…
EXTRA: Watch out for already declared vars in lambdas: while the parameter list
does declare new vars to be used in the body part of the lambda expression, these
variables won’t get new scope and, therefore, cannot shadow the previously declared
vars:
List al = new ArrayList();
System.out.println(check(al, al -> al.isEmpty())); // INVALID
Which LOC, when inserted at line r1, enables the code to print Passed: Alice, Doug, ?
A. checkScore(list, () -> e.getScore() > 65);
B. checkScore(list, Examinee e -> e.getScore() > 65);
C. checkScore(list, e -> e.getScore() > 65);
D. checkScore(list, (Examinee e) -> { e.getScore() > 65; });
Problem 9.40
Let sb refer to a StringBuilder object. Which of the following fail(s) compilation?
A. sb -> sb.toString()
B. StringBuilder sb -> sb.toString()
C. (StringBuilder sb) -> return sb.length();
D. (StringBuilder sb) -> { return sb.length(); }
class Interrogation {
interrogate(roundUp);
}
}
then overload the method interrogate() with the following code fragment:
private static void interrogate(List<Suspect> perps, LieDetector ld){
for(Suspect s : perps)
if(!ld.test(s))
System.out.println(s.getName() + " is lying");
}
then overload the method interrogate() with the following code fragment:
private static void interrogate(List<Suspect> perps, LieDetector ld){
for(Suspect s : perps)
if(!ld.analyze(s))
System.out.println(s.getName() + " is lying");
}
Problem 9.43
Which one is true?
A. Functional interface may not contain more than one method.
B. Any interface that has a single abstract method is therefore functional.
C. Functional interface cannot have superinterfaces.
D. Functional interface cannot be extended.
E. None of the above.
Problem 9.1 – D
Given:
class Hello {
public static void main(String[] args) {
String str = "Hello";
StringBuilder sb = new StringBuilder(str);
str.replace("o", ""); // h1
System.out.print(str + " "); // h2
System.out.print(str.replace("o","") + " "); // h3
System.out.print(sb.append("?").equals(sb.append("!")) + " "); // h4
System.out.println(str.replace("o","").equals(str.replace("o",""))); // h5
}
}
The first thing coming to your mind whenever you see a String object on the exam should be
something like "Ah! This animal is immutable, so I’d better be careful…" Naturally, there are
also some quirky methods to watch out for but, basically, this is it.
Now please list the immutable classes that are on our exam; we mentioned them when discussing
the solution to Problem 4.3… Did you get them all 10? Good.
Back to String. Since str is immutable, line h1 doesn’t change the original object; instead, a new
object is created – and wasted because the reference to it isn’t used in any way, so line h2 prints
the same old ‛Hello’. With a trailing space, of course. On the other hand, line h3 outputs ‛Hell ’
because the printing stat acts on the object that was returned by replace().
As for line h4, StringBuilder doesn’t override Object’s equals() → hence the method simply
compares two identical references and returns true. We should always keep in mind that
methods act on objects through a reference, so that’s why when the second append() completes
its job, both operands will contain the same sequence of characters, namely ‛Hello?!’. Let’s have
a simple illustration of this principle, this time with a class that does override equals():
class General{
int stars = 1;
General bePromoted(int stars){
this.stars += stars;
return this;
}
@Override
public String toString(){ return "" + stars; }
}
class ChiefOfStaff {
public static void main(String[] args) {
General g = new General();
System.out.println(g.bePromoted(1).equals(g.bePromoted(2))); // true
System.out.println(g); // 4
}
}
10
String + all LDTs + all Wrappers such as Byte, etc. + structurally-immutable List created by Arrays.asList().
Working with Selected Classe from Java API Chapter II.9 31
In the above example, the object is always the same, and any change made to it through a
reference gets reflected on both sides of equals().
Next step (we are getting back to the current Problem 9.1, by the way); what happens when we
have a mirror situation: the class String that, unlike StringBuilder, is not only immutable but
also overrides equals()? Line h5 answers that by printing true, but why? Specifically, is it
because String is immutable or for some other reason?
Alright, the first operand, namely str.replace("o",""), creates an identical twin to str, changes its
contents, delivers this freshly baked object to equals(), who grabs it, peeks inside and sees…
hmm… it sees Hell.
The second operand in its own turn takes the reference to the original and immutable str, which
still reads "Hello", does its thing and returns another new object that again contains "Hell". So on
both palms of equals() lie pointers to two different objects, which just happen to be made of the
same hellish stuff. Compare it to the situation with the mutable StringBuilder where append()
each time acts on and returns reference to the same object…
And what about an immutable object in a class that does not overrides equals()? Here we go:
class General{
int stars = 1;
// @Override
// public String toString(){ return "" + stars; }
}
class ChiefOfStaff {
Problem 9.3 – A
Given:
class TheFifthElement {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("1"); // earth
sb.append("2"); // water
sb.append("3"); // air
sb.append("4"); // fire
sb.replace(4,4,"Leeloo"); // line t1
System.out.println(sb);
}
}
StringBuilder does have a constructor that accepts a String, and its append() is overloaded for
all imaginable types.
Now, unlike the class String, StringBuilder’s replace() isn’t overloaded: its only version takes
three args, namely (int start, int end, String replacementString). In short, the code is perfectly
valid.
As with all the other methods in String and StringBuilder that accept two indices to limit a
specific part of the character sequence (such as str.substring(int beginIndex, int endIndex),
sb.append(CharSequence s, int start, int end), etc.), the first index is inclusive while the second
Problem 9.4 – C
Given the following class:
class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
String str = null; // line t1
for(int e : arr){ str += e; }
System.out.println(str);
}
}
String is a reference type and, as such, can be null, so the assop on line t1 is valid.
What about the next LOC? Let’s run the entire logical chain:
Step 1. str += e actually means str = str + e;
Step 2. JLS, §15.18, Additive Operators: "If the type of either operand of a + operator is
String, then the operation is string concatenation".
Step 3. JLS, §5.1.11, String Conversion: "If the reference is null, it is converted to the
string "null" (four ASCII characters n, u, l, l)".
→ so there’s neither a comperr nor RTE → this is why the first iteration results in ‛null1’ – and
everything after that is trivial.
11
Quoting from the StringBuilder API docs.
Specifically, your task is to finish up the method maskAccount(String accNum) so that the
class AccountMasker will output ****************67541 when run with the following
command:
java AccountMasker "34296 01853 49820 67541"
Which two LOCs, when used independently, will achieve this requirement?
A. return (new StringBuilder(accNum)).substring(15, 20).toString();
B. return lead + accNum.substring(15, 20);
C. return new StringBuilder(accNum).append(lead, 15, 20).toString();
D. return new StringBuilder(accNum).insert(0, lead).toString();
E. return new StringBuilder(lead).append(accNum, 15, 20).toString();
As usual, we start our analysis by looking at the available options: what if some of them can be
eliminated right away? It happens a lot on the exam, and this time is not an exception.
The output must contain asterisks (disguised as the var lead) → option A doesn’t even mention
that var → A is immediately out.
What about B? substring() works in the same way for both String and StringBuilder: it returns
a String and is overloaded for (int start) and (int start, int end) → we have the second version,
which returns whatever sits between index 15 inclusive and index 20 exclusive → what does this
method receive? → it receives the object returned by replace(" ", "") → to wit, a 20-digit
sequence now void of spaces because String’s replace() acts on each occurrence of the target
char or CharSequence → substring contains 67541 → it gets prepended with lead → bingo.
Now to option C. maskAccount()’s arg, represented by the local var accNum, contains 20 digits
without spaces → a new StringBuilder object gets created → it contains accNum → then
append() attempts to add to it five characters taken from lead → but in doing so it goes too far,
beyond the last available value since lead contains only 15 characters → an IOOBE.
Option D: insert() places lead at the first position in the StringBuilder object thus shifting to
the right entire bunch of whatever the object used to contain, namely a 20-digit string → the
result is way too long.
And since what’s left is just option E, we accept it without even analyzing its logic → but at the
Review stage we must do it → append() adds to lead a five-digit string extracted from
accNum’s tail → then converts the whole works to String and returns it → yeap, looks fine.
Since the answer to the problem is obvious, we can afford to spend a minute to take a look at the
bigger picture.
Only two classes on our exam have something that relates – either directly or indirectly – to this
particular creature called capacity, namely StringBuilder and ArrayList:
ArrayList StringBuilder
ArrayList() StringBuilder()
Constructs an empty list with an initial capacity Constructs a string builder with no characters in it
of ten. and an initial capacity of 16 characters.
Constructors ArrayList(int initialCapacity) StringBuilder(int capacity)
Constructs an empty list with the specified Constructs a string builder with no characters in it
initial capacity. and an initial capacity specified by the capacity
argument.
void ensureCapacity(int minCapacity) void ensureCapacity(int minimumCapacity)
Increases the capacity of this ArrayList Ensures that the capacity is at least equal to the
instance, if necessary, to ensure that it can hold specified minimum.
at least the number of elements specified by the
minimum capacity argument.
Methods void trimToSize() void trimToSize()
Trims the capacity of this ArrayList instance to Attempts to reduce storage used for the character
be the list’s current size. sequence.
int capacity()
Returns the current capacity.
Although it may sound so, capacity is not an upper limit of how much we can put into the
StringBuilder or ArrayList; it’s a value defining a boundary beyond which the internal array
must be reallocated to accommodate the necessary number of elements (in case of ArrayList) or
the CharSequence’s length (in case of StringBuilder). Whereas StringBuilder lets both get
and set its capacity, ArrayList does not provide a direct read access.
Back to our problem; option A doesn’t compile because String defines no constructor that would
take an int as its only arg; now please list the String constructors that are likely to appear on the
exam 12.
As for options B and C, they are just plain wrong. First of all, StringBuilder doesn’t have static
methods so the name of the class cannot be used in any invocation. Secondly, setCapacity()
doesn’t even exist in StringBuilder; lastly, either method would have to return a StringBuilder
object, and setters in a mutable class are usually void – just as setLength() is…
12
String(String str) and String(StringBuilder sb) + String(), which is virtually useless because the object is
immutable.
Of all the core Java classes we are supposed to learn for the exam only StringBuilder defines
insert() – which is indeed our case. However, this insert() in all its overloaded versions requires
at least two args (we need to specify what is to be inserted and where) → options B and C are
immediately eliminated.
Problem 9.8 – C
Given:
public static void main(String[] args) {
String a = "B ";
a = a.concat("U "); // line X
String b = "L ";
a = a.concat(b); // line XX
a.replace('U', 'A'); // line XXX
a = a.concat(b); // line XXXX
System.out.println(a);
}
We are dealing with String, which is immutable → the LOC on line XXX is immaterial as it
doesn’t assign the newly created object back to the var a. What is left for us is imitate the work
of lines X, XX and XXXX on paper → line X results in B U → then line XX appends ‛L’ to
what a holds → and line XXXX does the same.
Problem 9.9 – C
Given:
public class Simple{
public static void main (String[] args){
char c = 6;
System.out.println("Hello".charAt(c));
}
}
String’s charAt() takes an int, and Java treats char as an integral type representing values from
0 to 65535 inclusive → there is no comperr.
As for what happens at run time, since ‛Hello’ contains only five characters, the code attempts to
access something that doesn’t exist → IOOBE (or, to be more precise, a SIOOBE).
Problem 9.10 – D
Given:
StringBuilder bucket = new StringBuilder("Empty me!");
This problem is obviously about one of the easily confused methods. Consult this table among
the rules that relate to Exam Objectives 9.1 & 9.2 to refresh your memory, when in doubt.
Neither String nor StringBuilder nor ArrayList has an empty() method. The method clear()
belongs to ArrayList, and same goes for remove() and removeAll() → we are left to choose
among the three versions of delete().
deleteAll() is there to confuse you; there’s no such method, it simply bears a resemblance to
ArrayList’s removeAll().
Finally, as size() is defined in ArrayList while StringBuilder – together with String – uses
length(), we arrive at the correct conclusion that the answer to our Problem 9.10 is option D.
Problem 9.11 – B
Given:
class PoorGirl {
public static void main(String[] args) {
String name = "Javeline";
System.out.println("Hi! I’m " + name.replace("e", "a"));
}
}
Problem 9.12 – D
Given the following main() method:
public static void main(String[] args) {
String str = " ";
str.trim();
System.out.println(str.equals("") + " " + str.isEmpty());
}
The Strings created with double quotes are placed into a common string pool; everything else –
such as Strings instantiated with the new keyword or computed at run time – isn’t…
The above sentence is just a reminder; it’s not directly relevant to our Problem because
str.trim() returns a completely new object (but only if there were changes, that is), which in our
case doesn’t get used at all → str still points to the same object.
Now, it is immaterial whether or not the Strings " " and "" live in a common pool: they are
definitely distinct → and since equals() is overridden in String, it compares different contents.
Problem 9.13 – B
Given:
StringBuilder sb = new StringBuilder();
sb.append("Duke");
The first printing stat appears quite natural. As for append(), it works with the tail only, so even
its args look suspicious.
add() gets used so often that by now we should remember that it is a dweller of the ArrayList
realm.
Problem 9.14 – C
Given the following code fragment:
String str1 = "null";
String str2 = new String("NULL");
System.out.println(str1.equalsIgnoreCase(str2.toLowerCase())); // line 1
System.out.println(str2 == str2.replace('L','l').toLowerCase()); // line 2
System.out.println(str1 == str1.replace('L','l').toLowerCase()); // line 3
Problem 9.15 – D
Given:
public class TheMatrix {
public static void main(String[] args) {
String movie = "The";
movie.concat(" ").concat("MATRIX".toLowerCase());
System.out.print(movie.substring(5,6));
}
}
13
ArrayList has no replace(): it uses set(int index, E element) instead. StringBuilder defines replace(int start, int
end, String str) and doesn’t overload it.
A String? and the reference to it doesn’t get reassigned? → same object all along.
And whoever wrote that substring() invocation was obviously half asleep for the method wants
to grab the 6th character while movie still has only three.
Problem 9.16 – C
Given:
public class TheMatrixReloaded {
static void reload(StringBuilder sb) {
sb.append(" Matrix");
sb.insert(" Reloaded", sb.length());
}
public static void main (String[] args) {
StringBuilder sb = new StringBuilder("The");
reload(sb);
System.out.println(sb);
}
}
Many people prefer to read code starting from the main() method and then just following the
program’s business logic. The questions on our exam aren’t long (with a possible exception of
lambda-related problems – soon you’ll see what I mean) so it is also possible to start on the top
and then work your way to the bottom. Either way, you should notice that the invocation of
insert() is wrong: its args switched places.
Otherwise the answer would have been option B because StringBuilder is mutable, and we do
remember that Java is pass-by-value.
Problem 9.17 – E
Given the code fragment:
public static void main (String[] args) {
String[] str = new String[2];
int i = 0;
for (String e : str)
System.out.print(e.concat(" " + i++).trim());
}
str is an array, and arrays, when allocated, always get inited with default values in their slots. In
our case, the slots are of type String, which is a reference type, hence the default value is null.
Invoking a method on a null object is one of the most typical NPE-throwing scenarios on the
exam.
Problem 9.18 – B
Given:
public class Exam {
String str = ""; // line e1
static void pass(String str) {
str.concat("Passed");
}
public static void main(String[] args) {
String str = "Failed ";
pass(str);
System.out.println(str);
}
}
The var str declared in the method main() is local and, therefore, shadows the instance var str
on line e1, which is a good thing because pass() is static → the code doesn’t throw a comperr.
The method concat() is invoked correctly: it is overloaded for any data type so accepting a
String isn’t a problem, and the object itself isn’t null.
If the class String were mutable, the method pass() would have returned ‛Failed Passed’ – but it
isn’t, so str.concat() dutifully creates a new object, which is then simply wasted.
Problem 9.19 – B
Given:
public class AnotherExam {
public static void main (String[] args) {
StringBuilder sb = new StringBuilder("Passed");
System.out.print(sb + ": ");
System.out.println(sb.replace(0,4,"Fail") ==
sb.delete(0,666).insert(0,"Failed"));
}
}
StringBuilder’s replace() is not overloaded, takes three args (int start, int end, String str) and
returns the reference to the current object, so the left-hand side of == is valid → the object
contains now ‛Failed’.
Now to the right-hand side: StringBuilder’s delete() isn’t overloaded, either, and takes two args,
namely (int start, int end). A question arises: is it OK if the end index is way beyond the object’s
boundary? won’t the code throw an IOOBE?
No, says the API documentation: "[the method] throws StringIndexOutOfBoundsException if
start is negative, greater than length(), or greater than end" → the method successfully deletes
the entire string → and then insert() – which is overloaded, by the way – slips in ‛Failed’ at the
first position.
Whatever the object contains is, however, immaterial as == compares references and we are
dealing here with a mutable data type → the last printing stat outputs true.
Problem 9.20 – B
Given:
public class Capricchio {
public static void main (String[] args) {
Object obj = null;
StringBuilder sb = new StringBuilder(); // line c1
sb.append(obj);
System.out.println(sb.length());
}
}
Line c1 creates a StringBuilder object with no characters in it and an initial capacity of 16. As
for the method append(), it is overloaded for all imaginable types → its operation is therefore
successful.
But what exactly does it append? The mike goes to the API documentation:
append(Object obj) appends the string representation of the Object argument.
Next question: how this ‛string representation’ thingy is obtained? In a traditional way, that is, by
calling toString() on obj? If so, this will surely throw an NPE… Option E, then?
Problem 9.21 – C
Given:
public class HowAboutThisOne {
public static void main (String[] args) {
String str = null;
StringBuilder sb = new StringBuilder(str += str);
sb.delete(0,sb.length());
System.out.println(sb.capacity());
}
}
How many LOCs, when inserted independently at line XXX, will make the code print
Strings Forever!?
A. None
B. One
C. Two
D. Three
First and foremost – please do pay attention, this one is important – String has neither delete()
nor insert(); they belong to StringBuilder. The only methods that appear to change a String
structurally are concat(), replace() and trim() → the first two LOCs are definitely out.
LOC number three, while valid, isn’t what we need: it does remove the string " Bean" but the
computed result gets wasted because the hasty slogan writer forgot to assign it back to str.
Now, I don’t know about you, but in my eyes the last line of code looks entirely logical: it first
constructs an SB by passing a String object to the correctly chosen constructor, them remove()
gets rid of an extra " Bean" after which toString() converts the result to a String and assigns it to
str which then gets printed → this one should apparently work… only it doesn’t because
StringBuilder has no remove() → the last LOC is invalid.
Rather confusing, if you ask me. So, when preparing for the exam, I tried to come up with a
single logical rule:
No delete() or insert() in String → they are defined in StringBuilder only → remove() would
be totally redundant in SB because the class already has delete() that takes care of the task.
Or on a lighter note,
Rules for String are mean ’n tough,
Learn by heart this bloody stuff:
No delete() and no insert() –
That’s the way to Java cert.
add() to List to feel no pain,
Use remove() – or clear() its brain.
And remember that replace()
Can explode right in your face:
While in String it overloads,
SB follows other roads.
HTH.
Although it is a fact that something like while(true) may lead to a comperr if the loop is
followed by other statements, which become therefore unreachable, it doesn’t happen in our case
because the evaluation of equals() happens at run time.
The expression str+1 is also valid since the + op is overloaded for String; it simply appends 1
to the already existing string at each iteration, creating another String object – every time anew:
final String str = "";
int a = 0;
while(str.equals("")) {
System.out.println((str+1).hashCode()); // same number over and over again
a++;
if (a > 4) break;
}
As for the final keyword, it is actually irrelevant since the new object never gets assigned to the
original reference – unlike, for example, the following code snippet, which throws a comperr
because str was declared final:
final String str = "";
// System.out.print(str+=1); // INVALID: ’cannot assign a value
// to final variable str’
One more word of caution: Java doesn’t require braces around loop bodies, which not only might
break business logic if the coder isn’t attentive but also can create endless loops when a
semicolon is placed behind, for example, while(smth_that_evaluates_to_true) out of habit:
final String str = "";
while(str.equals(""));
System.out.println(str+1);
Similarly to our Problem 9.23, this code fragment also compiles and runs indefinitely although
without any visible output. What a nasty, well hidden trap… Fortunately, the exam seems not to
abuse semicolons but you should be on your guard just the same.
Problem 9.24 – B
Given:
"a".replace("a","b"); // line 1
"a".replace('a','b'); // line 2
"a".replace(0,"a".length(),"b"); // line 3
"a".replace(new StringBuilder('a'),""); // line 4
"a".replace(new StringBuilder('a'), new StringBuilder("b")); // line 5
new StringBuilder("a").replace("","b"); // line 6
new StringBuilder("a").replace('a','b'); // line 7
new StringBuilder("a").replace(0,1,"b"); // line 8
The literal "a" is a full-fledged String object → does String have the method replace()? → yes,
it does but there are only two overloaded versions, either of which takes two args → line 3 is
definitely invalid. It would have been fine, though, if it were invoked on a StringBuilder object
because StringBuilder does have replace() that accepts three args: two ints – start and end –
and a String.
What about line 4? It looks mighty odd: the args are of different – and even incompatible
types… Will it compile? To find out what’s going to happen, we need to test our knowledge of
the replace() method in String. Please answer: what are the arguments for replace() in String?
By looking at our code (lines 1 and 2), it is tempting to say: "two Strings and two chars".
Close, very close – but no cigar… because the API actually defines replace(char oldChar, char
newChar) and replace(CharSequence target, CharSequence replacement).
String and StringBuilder both implement the interface CharSequence (we have already met
this when discussing Problem 6.34); this is precisely why we can use Strings as arguments in
str.replace() – and same goes for StringBuilder objects. What’s more, StringBuilder does
define a constructor that accepts a CharSequence → line 4 is valid. By the same token, line 5
also compiles.
Alright, what can be said about line 6? Ah, but this one should be easy by now: StringBuilder
has no replace() that would take two args → line 6 is invalid → line 7 fails compilation, as well.
As for the last LOC, it looks clean: three args of correct types in correct places…
One more thing about replace() that you might find useful on the exam is that the method returns
a reference to the same object if there was no change:
String str = "h";
System.out.println(str == str.replace('Z','a')); // true
System.out.println(str); // h
System.out.println(
str == str.replace(new StringBuilder("Z"), new StringBuilder("a"))); // true
System.out.println(str); // h
Boring, eh? Getting on your nerves already… Alright, let’s play with replace() for the very last
time. Please riddle me this:
Given:
String str = "_";
str = str.replace(new StringBuilder('Z'), new StringBuilder("^"));
System.out.println(str);
14
As there’s no StringBuilder constructor that would take a char per se, 'Z' gets widened to int, so new
StringBuilder('Z') creates just an empty StringBuilder with capacity of 90. Effectively, the replace() invocation
reads replace("","^") – and "" matches boundaries of "_" because of the way the regex engine works…
Working with Selected Classe from Java API Chapter II.9 47
Problem 9.25 – A
Given:
public class Dissonance{
public static void main(String[] args) {
Object obj = "Quartet No. 19 in C Major, K. 465"; // line D1
System.out.println(obj.getClass().getSimpleName() + " " + obj); }
}
Is it true that the code prints String Quartet No. 19 in C Major, K. 465?
A. true
B. false
The question strongly hints on the most famous string quartet by Mozart so answering it should
be easy. A bit harder is figuring out why the code compiles and works as intended because we
get used to the idea that employing double quotes as the way to create objects is reserved for
Strings only, from which sort of follows that the reftype must be also String – and this is not so.
As soon as we realize that the double-quoted literal "String" is just a handy replacement 15 for
char string[] = {'S', 't', 'r', 'i', 'n', 'g'};
String str = new String(string);
it becomes clear as day that there’s nothing wrong with line D1.
Problem 9.26 – C
Given the following code fragment:
LocalDate today = LocalDate.of(2016, Month.JUNE, 13);
today.plusHours(24);
System.out.println(today);
Problem 9.27 – E
Given:
public static void main(String[] args) {
String date = LocalDate.parse("2016-07-13")
.plusDays(31)
.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(date); }
15
Ref.to the API javadoc's preambule for the class String.
Problem 9.28 – D
Given:
LocalDate ld = LocalDate.of(2016, 6, 13);
ld.plusMonths(6).format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(ld);
Now all the LOCs are clean, and the code does output the date contained in the object ld – which
is, of course, immutable but we already know that. The only question is what form this date will
be printed in: after all, the code compiles as can be easily deduced from the list of options.
Alright, the API documentation defines the class LocalDate in the following way:
A date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03.
This is it; by default, the date is output as YYYY-MM-DD; notice two mandatory digits for both
months and days. To get something like option A or B, we’d need to call on our LocalDate
object the method format() with an appropriate DateTimeFormatter as its argument…
I wonder what are you going to make out of this little gem?
This is oh-so-praised Kaplan: the software officially endorsed by both Oracle and Pearson Vue.
That’s how those guys propose – quoting – "to prepare you to pass the 1Z0-808 exam". Well
then, be prepared…
Problem 9.30 – A
Given:
System.out.println(LocalDate.now().plus(Period.of(0,0,0)));
System.out.println(LocalDate.of(2016, Month.JUNE, 13)
.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(LocalDate.parse("2016-06-13", DateTimeFormatter.ISO_DATE));
All three LOCs correctly create their corresponding LocalDate objects. Yes, it is that simple.
And even a simpler version of this question you are going to meet on the exam. Congrats.
Problem 9.31 – AD
Given the full contents of the file JavaBirthday.java:
1 package birthday;
2 import java.time.LocalDate;
3 import java.time.format.DateTimeFormatter;
4
One of the LOCs fails compilation. Which two modifications, used independently, will
make the code print Java was born on May 23, 1995?
A. Replace line 2 with import java.time.*;
B. Replace line 3 with import java.time.format.*;
C. Replace Month.MAY on line 7 with Month.May
D. Replace Month.MAY on line 7 with 05
The LDT classes often borrow things from other classes and even different packages, such as
DateTimeFormatter, which is defined in java.time.format. By using fully qualified names
rather than wildcards in imports, the code can create a situation when something is amiss.
That’s exactly what’s happening here: line 7 invokes the method of(), which takes as its second
arg a constant that is defined in the enum Month, which belongs to java.time. Using a
‛wildcarded’ import stat restores the compilability of the class.
Another way to get rid of the comperr is to use a two-digit int for the desired month instead of
the enum constant.
Problem 9.32 – A
Given the following class definition:
class LangsToLearn {
public static void main(String[] args) {
List<String> langs = new ArrayList<>();
langs.add("Ruby");
langs.add("Perl");
langs.add("Perl");
langs.add("Closure");
if (langs.remove("Perl")) langs.add("Emacs Lisp");
System.out.println(langs);
}
}
There is no reason for the compilation to fail since all methods are invoked correctly and the
object itself is also created in a valid way. Note, however, that it can contain only Strings. And
don’t forget that unlike StringBuilder, ArrayList defines remove() rather than delete().
Another important thing to keep in mind is that remove() acts on the first match only (compare it
to replace() in String).
This useful functionality is possible because List is an ordered collection. Which can also be
sorted. By the way, what’s the difference between ‛ordered’ and ‛sorted’? This is what the List
API documentation says:
Working with Selected Classe from Java API Chapter II.9 51
public interface List<E> extends Collection<E>
An ordered collection (also known as a sequence). The user of this interface has precise control over
where in the list each element is inserted. The user can access elements by their integer index
(position in the list), and search for elements in the list.
In other words, ‛ordered’ means in practice ‛addressable’ while ‛sorted’ implies that the elements
are physically placed in a certain order, which is imposed by the interface Comparable and is
referred to as ‛natural ordering’. The natural ordering of elements is specific for each class that
implements Comparable. For example, Strings are sorted lexicographically:
List<String> list = new ArrayList<>();
list.add("1"); list.add("03");
list.add("20"); list.add("123");
list.add("ah"); list.add("Zed");
Collections.sort(list);
System.out.println(list); // [03, 1, 123, 20, Zed, ah]
Back to our Problem 9.32; ArrayList’s remove(Object obj) returns true if the list contained
the specified object → "Emacs Lisp" gets added to the list.
Problem 9.33 – AF
Given:
ArrayList<String> someTypes = new ArrayList<>();
someTypes.add("byte");
someTypes.add("long");
someTypes.add("int");
Which two expressions evaluate to 3?
A. someTypes.size();
B. someTypes.capacity();
C. someTypes.length();
D. someTypes.get(2).size;
E. someTypes.get(2).length;
F. someTypes.get(2).length();
ArrayList doesn’t have the method capacity() (which is StringBuilder’s member) although it
defines ensureCapacity(); length() belongs to both String and StringBuilder.
As for the method get(), it returns the element at the specified position, and we then invoke
length() on it because, after all, our ArrayList is generified to String.
Let’s note in passing that option D wouldn’t have compiled even if someTypes were generified
to List and contained three three-element-long Lists because the number of elements in
someTypes must be computed by calling a method rather than by accessing a field.
As for option E, it would have worked, for example, in the following case:
ArrayList someTypes = new ArrayList();
Object obj = new Object();
int[] arrInt = new int[0];
String[] arrStr = {"","",""};
someTypes.add(obj);
someTypes.add(arrInt);
someTypes.add(arrStr);
System.out.println( ((String[])someTypes.get(2)).length ); // 3
Even though the right-hand side of the list-creating LOC is missing angle brackets, it’s OK
because the compiler can infer our intentions.
As type safety via generics is provided at compile time, the compiler looks at the reftype only
and disregards the diamond op on the right:
List list1 = new ArrayList(); // accepts anything
List list2 = new ArrayList<>(); // ditto
List list3 = new ArrayList<String>(); // ditto
On the other hand, the diamond on the left may not be empty → otherwise a comperr:
// List<> list4 = new ArrayList(); // INVALID
// List<> list5 = new ArrayList<>(); // INVALID
// List<> list6 = new ArrayList<String>(); // INVALID
Problem 9.35 – B
Given:
class TestingArrayList {
public static void main (String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 5; i++) list.add("" + i);
System.out.println(list.remove(list.indexOf("4")));
}
}
The invocation list.add("" + i) works correctly because our list is generified to String and
the op + is overloaded for Strings. The methods indexOf() and remove() are also called in valid
ways → option D is out.
remove() receives an int whose value is 4 (since elements are enumerated starting with zero) →
remove(int index) strips list of the element at the specified position and returns it (the element,
that is) → the code prints 4.
In short, it is important to remember that remove(int index) returns what was taken out from the
List and not, for example, a boolean as if to say ‛Alright boss, I successfully did what you have
requested’. Because it doesn’t need to: it either removes an element or reports a failure by
throwing an IOOBE, that’s all. On the contrary, the overloaded version of the method, namely
remove(Object obj), does return a boolean…
Please also note what kind of loop was used to populate the list. It may be tempting to employ an
enhanced for, like this:
List<String> list = new ArrayList<>();
int i = 0;
while(i<5)
for (String e : list) list.add("" + i++);
System.out.println(list.remove(list.indexOf("4")));
but it won’t work: list is still empty, there’s no elements to iterate over → for won’t run, and the
code will enter an endless loop because the loop var i has no chance to increment…
Problem 9.36 – B
Given:
public class MutatisMutandis {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new StringBuilder(""));
list.add("");
for (Object e : list )
if (e instanceof StringBuilder) ((StringBuilder)e).append("OK");
else ((String)e).concat("OK");
System.out.println(list);
}
}
As list isn’t generified, it accepts any object. Casts ensure that the code may call methods
specific for either StringBuilder or String. And finally, since String is immutable, invoking the
method concat() doesn’t affect the original String object, which remains empty.
Using an empty diamond in the right-hand side of the stat on line b1 doesn’t affect code’s
compilability, so aviary gets successfully populated with virtual birds whose reftype is Object
and the actype String. Line b2 is valid, as well; it replaces the first element with String "jay",
and since the method set() returns what was replaced, add() appends "kinglet" to the list.
Problem 9.38 – C
Given:
class Sweet16 {
public static void main(String[] args) {
List<Integer> ages = new ArrayList<>();
ages.add(16); // line s1
ages.add(null);
for (int i = 0; i < ages.size(); i++) System.out.print(ages.get(i));
for (int i : ages) System.out.println(i);
}
}
List autoboxes primitive args, so line s1 actually appends an Integer. Adding null to ages isn’t
problematic, either. What happens next, however, is: the first for loop behaves itself but the
enhanced for throws an NPE. Let’s see why.
As get(i) returns an Integer, the printing stat receives what Integer’s toString() returns → the
first for outputs 16null. The enhanced for successfully unboxes the first element and assigns it
to a primitive, but a similar attempt with null fails. Replacing int with a compatible reference
type would have cleared up the problem.
Conclusion: using a primitive loop var in an enhanced for can lead to an NPE when iterating
over a List → always make sure that the loop var is suitable for the task.
class ReleasingResults{
public static void checkScore(List<Examinee> list, Predicate<Examinee> p){
for (Examinee e : list) {
if (p.test(e)) {
System.out.print(e.getName() + ", ");
}
}
}
// line r1
}
}
Which LOC, when inserted at line r1, enables the code to print Passed: Alice, Doug,?
A. checkScore(list, () -> e.getScore() > 65);
B. checkScore(list, Examinee e -> e.getScore() > 65);
C. checkScore(list, e -> e.getScore() > 65);
D. checkScore(list, (Examinee e) -> { e.getScore() > 65; });
The Problem looks rather demanding but, in fact, lambda-related questions are arguably among
the easiest ones on the exam. As always, we start our analysis by glancing at the list of available
options, and a bunch of lambda expressions immediately indicates what the question is about.
The exam requires us to work with predicative lambdas; we just need to make sure if this is the
case; the checkScore() method’s signature confirms it.
Since the interface Predicate’s test() method takes a single arg, option A is out. Option B is also
invalid because the explicit use of the parameter type requires parentheses.
Option D fails compilation because semicolon, braces and return should be used together 16 –
but return is missing.
16
One more time: please keep in mind that this rule is applicable only on our exam where we are supposed to deal
with predicative lambdas. The interface Predicate’s test() returns a boolean → hence return is mandatory → but
when the functional method is void, the Semirebra rule won’t work.
The question hints that the list of options may contain more than one invalid LOC so we’ll be
needing to check every option. All four cases indicate that the lambda method takes a single arg
of type StringBuilder and returns either a String or an int – and this info is enough to start the
analysis.
Option A: the formal parameter has no explicit data type but this is OK; the method body
contains a single String-returning stat so braces, semicolon and return aren’t necessary →
option A looks perfectly compilable.
What about option B? While the method body appears to be correct, the parameter list is missing
parentheses, which are now mandatory because of the explicitly stated data type.
Option C is also invalid since it is omitting braces; option D, on the other hand, dutifully follows
all the rules: the parentheses around the paramlist are in place, and the method body is written in
its full form, with braces, return and semicolon → only options B and C fail compilation.
Let’s see if our conclusion is indeed correct:
interface Lambdable1{ String run(StringBuilder sb); }
interface Lambdable2{ int run(StringBuilder sb); }
class Test{
static void test1(Lambdable1 l){
System.out.println(l.run(new StringBuilder("lambda")));
}
static void test2(Lambdable2 l){
System.out.println(l.run(new StringBuilder("lambda")));
}
public static void main(String[] args) {
test1( sb -> sb.toString() ); // lambda
// test1( StringBuilder sb -> sb.toString() ); // INVALID
// test2( (StringBuilder sb) -> return sb.length() ); // INVALID
test2( (StringBuilder sb) -> { return sb.length(); } ); // 6
}
}
Problem 9.41 – AE
Given:
class Test {
String check(List list, Predicate p){ // line t1
return p.test(list)? "Empty" : "Populated";
}
void run() {
ArrayList list = new ArrayList(); // line t2
System.out.println(
check(list, list -> list.isEmpty())); // line t3
}
public static void main(String[] args) {
new Test().run();
}
}
First of all, we need to see why the original code doesn’t compile. All options mention line t3; is
this LOC problematic? Indeed, it is: its paramlist clashes with the already declared variable list.
Renaming the parameter saves the day → option A (incidentally, it completely disregards the
functional method's argument and accesses list directly, then prints Empty) is in.
What about option B? It changes the object’s reftype to a wider one but how can this help when
myList is passed to test() that sees it as an Object only? After all, Object has no isEmpty()
method…
All the other options hint that there may be something wrong with the compatibility of the data
types in the signature of the check() method and its invocation.
Why? Because line t1 specifies ungenerified List and Predicate → they will accept any object
→ expect illegal implicit downcasts and CCEs at run time.
We can use the following rule of thumb:
A predicative lambda? Check its type first! Object, Predicate and the formal parameter are ideally
should be of the same type; no sub/superclasses combos for Predicate and the parameter.
Let’s see how the rule works in practice; we’ll start with something simpler: will this print true?
class Test{
public static boolean checkList(List list, Predicate<List> p){
return p.test(list); // line X
}
public static void main(String[] args) {
boolean boo = checkList(new ArrayList(), (ArrayList al) -> al.isEmpty());
System.out.println(boo);
}
}
The answer is ‛No’ because the code fails compilation. Reason: Predicate is generified to List
→ Predicate’s test() expects List → but checkList() offers ArrayList al → won’t work; after
all, lambda expression’s ultimate raison d’être is to override the functional method…
Very well; what if we change Predicate<List> to Predicate<ArrayList>? Now lambda’s body
does override the method test() but the code still won’t compile since the invocation
p.test(list) specifies an arg of type List → we have an implicit downcast from List to
ArrayList on line X, which is illegal. On the other hand, an explicit cast such as
p.test(<ArrayList>list) works, and the code prints true.
Re-capping: Predicate<T> and paramlist (T param) must be of the same type T and the object
must be assignable to that type (or use a cast).
Now we can get back to our Problem 9.41; option C makes test() expect an ArrayList but
delivers a List → which cannot be converted to ArrayList implicitly → a comperr. Doing the
same thing with a cast in a strategic place would have helped, though:
String check(List list, Predicate<ArrayList> p){
return p.test((ArrayList)list)? "Empty" : "Populated"; }
Option D resolves the conflict of types between the check() method’s signature and the test()
method’s invocation; however, the fact that this option also changes the object’s reftype on line
t2 to List leads to the same problem: an illegal implicit downcast in the check() method’s
invocation. Casting mist would have cleared the problem:
check((ArrayList)mist, list -> list.isEmpty()));
Option E finally restores balance: the signature of check() specifies a List for both its args, and
the object created on line t2 is of type ArrayList, which IS-A List…
Problem 9.42 – D
Given:
class Suspect {
private String name;
private boolean statement;
class Interrogation {
private static void interrogate(List<Suspect> perps){
for(Suspect e : perps)
if (e.getStatement() != true)
System.out.println(e.getName() + " is lying!");
}
public static void main(String[] args) {
List<Suspect> roundUp = new ArrayList();
roundUp.add(new Suspect("Alice"));
roundUp.add(new Suspect("Bob"));
roundUp.add(new Suspect("Charlie"));
roundUp.add(new Suspect("Doug"));
roundUp.add(new Suspect("Eugine"));
roundUp.add(new Suspect("Frances"));
interrogate(roundUp);
}
}
then overload the method interrogate() with the following code fragment:
private static void interrogate(List<Suspect> perps, LieDetector ld){
for(Suspect s : perps)
if(!ld.test(s))
System.out.println(s.getName() + " is lying");
}
then overload the method interrogate() with the following code fragment:
private static void interrogate(List<Suspect> perps, LieDetector ld){
for(Suspect s : perps)
if(!ld.analyze(s))
System.out.println(s.getName() + " is lying");
}
As the available options make use of not only predicative lambdas but also lambdas in general,
you will not meet such a question on the exam; practicing with fundamental principles, however,
can be beneficial, so let’s do it unhurriedly and methodically.
To have a lambda, we’ll be needing a functional interface, in other words, an interface with a
single abstract method. Although this handy definition isn’t entirely correct – as you’ll see in the
very next Problem 9.43 – it’s good enough in practice.
Example 1:
interface Lambdable{
void doStuff(String str);
}
interface AnotherLambdable{
String doStuff(String str);
}
class Test {
static String str = "hello";
static void run1(String str, Lambdable l){
l.doStuff(str);
}
static String run2(String str, AnotherLambdable al){
return al.doStuff(str);
}
In Example 1 we defined our own functional interfaces; one of them, Lambdable, has a void
method → no invocation of run1() may mention return that actually returns something; the
fourth, invalid invocation would have worked if run1() were replaced with run2().
Time to play with the interface Predicate. Coming from the field of linguistics, I couldn’t at first
get the whole notion through my skull because to me a predicate meant either a verb or a verb
phrase. For example, in the sentence ‛It doesn’t compute’ the predicate is ‛doesn’t compute’, and
in ‛Lambdas are weird’ the predicate is ‛are weird’. And how exactly can it help to conquer those
LEs? As for the attempt to get enlightened by asking Wiki, it only made my head spin…
Getting back to our Problem. Option A suggests to overload the method interrogate() with:
private static void interrogate(List<Suspect> perps, Predicate p){
for(Suspect s : perps)
if(!p.test(s))
System.out.println(s.getName() + " is lying");
}
The code compilability analysis on the exam should start with the verification of the lambda
method body followed by signature check. Yes, in this order because the exam question will
most likely test you on the correctness of the lambda expression rather than the compatibility of
the involved objects.
17
Not available in English; there’s a German translation, though: Wörterbuch der Logik von N.I.Kondakow. The
book is awesome, it helped me untold number of times…
The signature specifies an ungenerified Predicate → as the result, the method test() sees only an
Object instead of the promised Suspect → Object has no getStatement() method → a comperr.
Now to option B; it wants us to overload the method interrogate() with the following:
private static void interrogate(List perps, Predicate<Suspect> p){
for(Suspect s : perps)
if(!p.test(s))
System.out.println(s.getName() + " is lying");
}
and use the same method invocation as in option A (which we have already ascertained as being
valid) → skipping the lambda expression verification → applying signature verification rule →
Predicate is generified to Suspect whereas the object perps’ reftype is an ungenerified List →
but the enhanced for will want to use perps’ elements as Suspects rather than Objects → a
comperr because of an implicit downcast from Object to Suspect, which is illegal.
If we only had a carte blanche, we still could, theoretically speaking, save the day by making the
enhanced for to iterate over Objects rather than Suspects and then apply a couple of casts in
strategic places, like this:
private static void interrogate(List perps, Predicate<Suspect> p){
for(Object s : perps) // line X
if(!p.test((Suspect)s)) // XX
System.out.println(((Suspect)s).getName() + " is lying"); // XXX
}
Line X gets rid of the comperr that reads ‛incompatible types: Object cannot be converted to
Suspect’; the cast on line XX gives the method test() what it wants; and the cast on line XXX
allows to invoke getName(). Option B, however, isn’t that flexible so we mark it as incorrect.
Now, option C. Ah, but this is fun! We get to create our own interface that should mimic
Predicate in java.util.function:
interface LieDetector {
default boolean test(Suspect s){ return s.getStatement(); }
}
Wait a minute… How come the method test() in LieDetector is declared as default?! it must
be abstract! No, this won’t do: our brand new interface isn’t functional → option C isn’t what
we need, either…
As we saw when analyzing option A, the invocation is valid. The interface is indeed functional:
it declares a single abstract method. The signature, however, looks suspicious because
LieDetector isn’t generified… Hmm… what could that mean?.. Ah! unlike test() in Predicate,
the method analyze() declares that it will accept only objects of type Suspect → this is a perfect
match with List<Suspect> → the enhanced for is valid → this option is indeed correct.
Now, what’s the story with Predicate’s test() and how it differs from our analyze()? Compare
the definitions:
interface Predicate<T> { boolean test(T t); /* plus four other non-abstract methods */ }
interface LieDetector { boolean analyze(Suspect s); }
In its current form, our LieDetector cannot be generified because <T> is missing; that’s why the
method analyze() was specifically tailored to Suspect. After making LieDetector generifiable,
we could use the already familiar idiom with the generified List and LieDetector in the method's
signature :
interface LieDetector<T> { boolean analyze(T t); }
class Interrogation {
// other necessary LOCs
private static void interrogate(List<Suspect> perps, LieDetector<Suspect> ld){
// enhanced for, printing stat, etc.
}
Problem 9.43 – E
Which one is true?
A. Functional interface may not contain more than one method.
B. Any interface that has a single abstract method is therefore functional.
C. Functional interface cannot have superinterfaces.
D. Functional interface cannot be extended.
E. None of the above.
@FunctionalInterface
interface Lambdable extends Interable{
default void walk(){}; // must override super’s walk to make it non-abstract;
// int run(); // when uncommented, makes Lambdable non-functional
boolean equals(Object obj);
}
@FunctionalInterface
interface Omegable extends Lambdable{}
class Test{
static void test(Lambdable l){
System.out.println(l.run(new StringBuilder("lambda")));
}
static void protest(Omegable o){
System.out.println(o.run(new StringBuilder("omega")));
}
public static void main(String[] args) {
test(sb -> sb.length()); // prints 6
protest(sb -> { sb.delete(0, sb.length()); // 5
return sb.append("alpha").length();
});
}
}
The program has two functional interfaces, namely Lambdable and its child, Omegable →
options C and D are incorrect.
Option A is obviously incorrect as Lambdable (and, therefore, Omegable) has three methods
(default walk() plus abstract equals() plus inherited abstract run(StringBuilder sb)), yet it
works as intended (just look at the @FunctionalInterface annotations to which the compiler
doesn’t object) → options A and B are also incorrect…
We were taught that
Conceptually, a functional interface has exactly one abstract method 18
which is a bit disconcerting as Lambdable has two abstract methods. As usual, to understand
what the heck is going on, we have to turn to the JLS (§9.8, Functional Interfaces):
The definition of functional interface excludes methods in an interface that are also public methods in Object.
This is to allow functional treatment of an interface like java.util.Comparator<T> that declares multiple abstract
methods of which only one is really "new" - int compare(T,T). The other method - boolean equals(Object) - is an
explicit declaration of an abstract method that would otherwise be implicitly declared, and will be automatically
implemented by every class that implements the interface.
So for an interface to define an abstract method that has its public namesake in Object
doesn't count toward being functional.
Well, basically this is it for lambdas. Since our book is also at its end, how about adding a couple
of extra touches to the discussion? and then we’ll call it quits…
Consider the following program, which tests certain animal species for their ability to swim, hop
or fly 19:
18
https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
19
You can find its listing without line numbers in Appendix C.
Working with Selected Classe from Java API Chapter II.9 65
1 package org.xlator;
2 import java.util.ArrayList;
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 class Animal{
7 private String species;
8 private boolean canHop;
9 private boolean canSwim;
10 private boolean canFly;
11 boolean getHop() { return canHop; }
12 boolean getSwim(){ return canSwim; }
13 boolean getFly() { return canFly; }
14
15 public Animal(String species, boolean canHop, boolean canSwim, boolean canFly){
16 this.species = species;
17 this.canHop = canHop;
18 this.canSwim = canSwim;
19 this.canFly = canFly; }
20
21 @Override
22 public String toString(){ return species; }
23 }
24
25 @FunctionalInterface
26 interface CheckTrait{ boolean test(Animal a); }
27
28 class TestHopper implements CheckTrait{
29 public boolean test(Animal a){ return a.getHop(); }
30 }
31
32 public class Filter {
33
34 public static void main(String[] args) {
35 List<Animal> animals = new ArrayList<>();
36 // swimmer hopper flier
37 animals.add(new Animal("fish", false, true, true ));
38 animals.add(new Animal("kangaroo", true, false, false ));
39 animals.add(new Animal("cat", true, false, false ));
40 animals.add(new Animal("dog", true, true, false ));
41 animals.add(new Animal("bird", true, true, true ));
42 animals.add(new Animal("turtle", false, true, false ));
43 animals.add(new Animal("rabbit", true, false, false ));
44 animals.add(new Animal("ladybug", false, false, true ));
45
46 Predicate<Animal> hop = a -> a.getHop();
47 Predicate<Animal> swim = a -> a.getSwim();
48 Predicate<Animal> fly = a -> a.getFly();
49
50 System.out.println("Can hop (old school):\n--------");
51 filterOldWay(animals, new TestHopper());
52
53 System.out.println("\nCan swim (with lambdas):\n--------");
54 filterNewWay(animals, a -> a.getSwim());
55 // filterNewWay(animals, swim); // more compact
56
57 System.out.println("\nCan fly (with .stream):\n--------");
58 animals.stream().filter(fly).forEach(a -> System.out.println(a));
59
60 System.out.println("\nCan both hop and fly (complex filter):\n--------");
61 animals.stream().filter(a -> hop.and(fly).test(a))
62 .forEach(a -> System.out.println(a));
63 }
64
65 static void filterOldWay(List<Animal> animals, CheckTrait checker){
66 for (Animal a : animals){
67 if(checker.test(a))
68 System.out.println(a); }
69 }
70
71 static void filterNewWay(List<Animal> animals, Predicate<Animal> checker){
72 animals.stream().filter(a -> checker.test(a))
73 .forEach(a -> System.out.println(a)); }
74 }
Several approaches are being used to check the traits: the ability to hop is tested in the old-school
fashion, so to speak, by explicitly creating an object that has a getter method that returns a
boolean; then we use a lambda expression to check animals for their ability to swim; note that it
can take two forms: we either write the LE directly on the LOC with the method invocation (line
54), or use the Predicate<Animal> var that holds the appropriate LE (the commented-out line
55). The two last checks (for the ability to fly and, after that, the ability to both fly and hop)
demonstrate the use of streams and complex, multi-stage filters, which are possible thanks to the
additional methods that the interface Predicate defines.
For example, replacing line 61 with the following:
animals.stream().filter(a -> hop.or(fly).test(a))
would have selected only those animals that can either hop or fly (i.e., fish, kangaroo, cat, dog,
bird, rabbit, and ladybug), while this LOC
animals.stream().filter(a -> hop.and(fly.negate()).test(a))
would have chosen those who can hop but are unable to fly (namely, kangaroo, cat, dog, and
rabbit). This is where our decision to assign the LEs to separately declared Predicate variables
comes in handy as the code becomes less cluttered and, therefore, more readable and
maintainable…