News

March/April 2017 issue of Java Magazine, “Quiz Yourself” (1z0-808, 1z0-809)

java-magazine-march-april-2017-issue
Written by Mushfiq Mammadov

In March/April 2017 issue of Java Magazine have been published four questions regarding 1Z0-808 (OCA Java SE 8) and 1Z0-809 (OCP Java SE 8) exams:

 

Question 1 (intermediate). Given this code (with line numbers at left), which is a fragment of a larger method body:

 StringBuilder[] sba = {
    new StringBuilder("Fred"),
    new StringBuilder("Jim"),
    new StringBuilder("Sheila")
 };

 System.out.println("sba[2] is " + sba[2]);

Which two of the following are true?

a. The array referred to by sba might be eligible for garbage collection at line 19.
b. The array referred to by sba might be eligible for garbage collection at line 21.
c. Assigning sba = null; at line 21 would make the array referred to by sba and the three StringBuilder objects definitely eligible for garbage collection.
d. The array referred to by sba and the three StringBuilder objects will definitely be eligible for garbage collection when the method returns to its caller.
e. The array referred to by sba and the three StringBuilder objects might not be eligible for garbage collection even after the method returns to its caller.

 

Question 2 (intermediate). Given the following code:

  • // line n1
    switch (x) {}

Which two of the following lines of code can be added successfully at line n1? Assume that x has no declaration in scope at line n1 and assume that each line is added individually.

a.  boolean x = false;
b.  short x = 99;
c.  int x = 0;
d.  long x = 0;
e.  StringBuilder x = new StringBuilder("x");

 

Question 3 (advanced). Given this:

public class Recorder {
    class Record {}
    class LongRecord extends Record {}

    public List<LongRecord> gatherRecords() {
        return Arrays.asList(
            new LongRecord(), new LongRecord()
        );
    }

    public void processRecords(
          Collection<Record> records) {  // line n1
       records.forEach(System.out::println);
    }

    public void gatherAndProcess() {
        List<LongRecord> lr = gatherRecords(); // line n2
        processRecords(lr);
    }

    public static void main(String[] args) {
        new Recorder().gatherAndProcess();
    }
}

Which is true? Choose one.

a. The code produces output in the following form:
Recorder$LongRecord@1218025c
Recorder$LongRecord@816f27d

b. Changing the argument type declaration at line n1 to List<Record> produces output in the following form:
Recorder$LongRecord@1218025c
Recorder$LongRecord@816f27d

c. Changing the argument type declaration at line n1 to Collection<? super Record> produces output in the following form:
Recorder$LongRecord@1218025c
Recorder$LongRecord@816f27d

d. Changing the argument type declaration at line n1 to Collection<? extends Record> produces output in the following form:
Recorder$LongRecord@1218025c
Recorder$LongRecord@816f27d

e. Changing the variable type declaration at line n2 to List<Record> produces output in the following form:
Recorder$LongRecord@1218025c
Recorder$LongRecord@816f27d

 

Question 4 (advanced). Given this code:

List<String> ls = Arrays.asList("Fred", "Jim", "Sheila", "Fred");
Set<String> s1 = new HashSet<>(ls); // line n1
Set<String> s2 = new TreeSet<>(ls); // line n2
System.out.println(s1.equals(s2));

What is the result? Choose one.

a.  Line n1 causes compilation to fail.
b.  Both line n1 and line n2 cause compilation to fail.
c.  An exception is thrown at line n1.
d.  Outputs true.
e.  Outputs false.

 

Answers
  1.  C, E
  2.  B, C
  3.  D
  4.  D

 

Explanations
Question 1

The correct answers are options C and E. The basis on which an object is eligible for garbage collection is simply that it cannot be reached from any live thread. In other words, the program can never get hold of the object again. At line 19, sba is an entirely valid reference to the original array. Consequently, sba is deinitely not eligible for collection at that point. That’s really important because if it were collected, the attempt to print sba[2] on line 20 would have unpredictable results. Generally, Java avoids any unpredictable behaviors (concurrent programming is the significant exception to this). This means that option A is incorrect.

Similarly, although sba does not appear to be used again at or after line 21, it is deinitely not eligible for garbage collection because the reference is still valid, and it could be used later in the method. Note that the question explicitly states that there is more to the method. Therefore, option B is also incorrect.

However, if line 21 executes sba = null;, the previous value of the reference is overwritten. Because the now-lost value in sba was the only reference to the array created in lines 14 through 18, and that value was never stored anywhere else (not even as an argument to a method call) between its creation and line 21, you know that there are now zero references to that object that are available to the program. As a result, the array is deinitely unreachable and is eligible for garbage collection. Also, references to the three StringBuilder objects were written only into the array, and because the array is not reachable in the program, neither are the StringBuilder objects, and they, too, are eligible for garbage collection. Because of this, option C is correct.

Options D and E are, perhaps obviously, the inverses of one another, so one must be true and the other false. As a side note, exam creators try to avoid this sort of situation —because it makes a question easier—but sometimes it is unavoidable, so it is worth spotting inverse answers. Be sure your logic is sound, though. For example, if option E had said “definitely not eligible,” the two would not have been inverses of each other, because the important possibility of “don’t know” would be a third possibility not covered by either.

In this case, because one or the other must be true, either you can definitely collect the objects at the return of the method or you cannot be sure. Which is it? Generally, local variables go out of scope and cease to exist when a method returns. This would suggest that the objects referred to by those variables become eligible for garbage collection. However, in this case, you don’t see the whole method body. If a reference to the array “escapes” from the method, you can make no such assumption. At least three mechanisms
exist by which this escape might occur, the most obvious of which is probably when the method itself returns the value of sba to its caller. A second possibility is that this method stores the value of sba in a variable that has greater visibility and longer life, such as a static variable or collection. In addition, passing sba as a method argument might allow the value to escape, because you cannot know if that method stores the value somewhere in a manner similar to the previous point. As a result, you cannot know if the array and, therefore, the three StringBuilder objects are eligible for garbage collection. Consequently, option E is true and option D is false.

Question 2

The correct answers are options B and C. The rules for a switch statement require that the argument be assignment-compatible with an int (allowing for autounboxing), an enum, or a String. Java Language Specification section 14.11 states that the type of the expression “must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type, or a compile-time error occurs.” String and enum types haven’t always been allowed; enum was new with Java 5. Therefore, it didn’t apply before that and, in fact, String was not legal until Java 7.

Although String is a legal type for a switch statement, StringBuilder is not; neither is it assignment-compatible with String.

Because of these rules, options A, D, and E are all incorrect, while both options B and C are correct.

Question 3

The correct answer is option D. This question delves into one of the most startling consequences of the type-erasure mechanism of Java’s generics system. When a variable of a collection type is declared with its generic specification—as in something like List<Record>—the “Record” part of the information exists only at compile time. The underlying object is still a List that actually accepts any object type. The power of generics is that they allow the compiler to do “consistency checking” that can ensure that type errors cannot happen in the code.

As part of the consistency checking, the compiler verifies that all assignments are safe from the perspective of the Liskov Substitution Principle. This is a principle in object orientation that requires that if you assign b = a, a must be capable of fully substituting for b. That is, all the behaviors expected of the type of b must be properly implemented, and work as expected, in the object referred to by a.

Now, the trick here is that, in fact, the compiler does not accept that assigning a List<LongRecord> to a Collection <Record> is a valid substitution. Because the assignment that occurs in the invocation of the method declared around line n1 is not valid, the code does not compile and option A is incorrect.

This question really hinges on why that assignment is not valid, and how you might correct that problem. Option B suggests that by changing the argument type from Collection to List, you might ix it. However, a List is fully capable of substituting for a Collection (the List interface extends Collection, and all the methods are implemented). This part of the assignment is not the problem, and the change wouldn’t alter the situation. Therefore, option B is incorrect.

Consider the following, because it’s a little less confusing. The compiler thinks that a List<LongRecord> is not a valid substitute for a List<Record>. There’s something that you can do with a List<Record> that isn’t properly supported by a List<LongRecord>. That illegal operation is the addition of a Record. Think about that for a moment. A List<LongRecord> can properly contain anything that’s assignment-compatible with a LongRecord: that would be LongRecord objects and any subclasses thereof. However, it should not contain any instances of the parent class Record. And it should not contain any sibling classes of LongRecord, if they existed. However, a List<Record> can legitimately contain such objects, and the compiler doesn’t know that you don’t add any in the method declared around line n1. Imagine the consequences if the method, prior to looping over the contents of Collection, invoked records.add(new Record()). That would be bad, right?

The code does not actually do any such terrible operations, but the compiler doesn’t analyze that; it just knows that the code could. Fortunately, there’s a syntax that lets you do what you want to do—that is, iterate over the contents of the collection, safely extracting things from it while knowing they’re assignment-compatible with Record. In efect, you define that Collection can be a collection of “anything that’s assignment-compatible with Record.” Therefore, if X is a type that’s assignment-compatible with Y, X must be “further down” the inheritance hierarchy (regardless of whether it is an object or an interface) than Y. In a sense, X extends Y. And that’s how the syntax comes about; the language lets you say, in effect, “collection of something,” provided that something extends Record. (Note that in this sense, Record “extends” Record because it’s assignment-compatible.) That syntax, of course, is Collection<? extends Record>. On that basis, option D is correct.

Option C is also interesting. In this situation, the proposed change would say that whatever Collection is intended to contain, Record must be assignment-compatible with that. If you presented a collection of Object, then Object is a superclass of Record, and you’d be able to insert the Record safely into Collection. However, you have no idea what types you might pull out of Collection, which might be a problem. But regardless of this, you still cannot pass List<LongRecord> into the method, because List<LongRecord> could not properly accept assignment of a Record into List even if you wanted to do that. Therefore, this syntax doesn’t solve the problem at hand, and option C is incorrect. However, this is an important syntax when you want to assign things to the generic type of whatever is passed in.

Option E fails for the same reasons the code fails in its unchanged form. The return type of the gatherRecords method is List<LongRecord>, and that’s not assignmentcompatible with List<Record>. Therefore, changing the variable type on line n2 would fail to compile, even though it would allow the invocation of the method around line n1 to compile. Substituting one problem for another doesn’t result in any output, though, so option E is incorrect.

Question 4

The correct answer is option D. Options A and B suggest compilation failures while attempting to construct and initialize the sets. This might happen if there is no constructor available that accepts a List as an argument. However, both Set types provide such a constructor. The documentation for collections calls for collection implementations to provide constructors that accept an argument of type Collection, and as a result, a List is valid. Because of this, both options A and B are false.

Option C suggests that an exception is thrown during construction of the Set. This might be plausible, given that the List has a duplicate entry, which is not permitted in a Set. However, the documentation notes that “all constructors must create a set that contains no duplicate elements.” The efect is that both Set objects contain only the three distinct elements, “Fred”, “Jim”, and “Sheila”. It’s also fair to observe that, in general, a Set recognizes and ignores attempts to add duplicates, so an exception in this initialization situation would be unhelpful and counterintuitive. Because no exception is thrown, option C is false.

As a side note, it’s possible for a TreeSet to throw an exception during the addition of items, if the items being added to it are not Comparable and no suitable Comparator has been provided. In this case, however, the items being added are String objects, and String implements Comparable<String> as needed, so no such problem arises.

Having eliminated all the other options, options D and E are essentially alternatives. The interesting thing here is that the documentation for the equals method of Set requires that all implementations return a value that indicates only whether the contents of the set are the same, without regard to the implementing class. As the documentation notes further, “This deinition ensures that the equals method works properly across diferent implementations of the set interface.” As a result, the output is the value true, and option D is correct, while option E is incorrect.

 

Source: March/April 2017 issue of Java Magazine

About the author

Mushfiq Mammadov

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.