Java Generics Flashcards

1
Q

Collection extends Number> is a GET-ONLY subtype of Collection where T is subtype of Number

REMOVE considered as GET

A

When you see

void foo(Collection  extends Number>) {
}

It says this function accepts collections of Integer|Double… to READ/GET (include remove) but not to add new element (How is this enforced by JVM?)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

The Java Generics Get (? extend from T) and Put (? super of T) Principle

Get|Read via casting (concrete-type pointer)
Put|Write via void* (opaque pointer)

A

List extends T> e.g. List extends Number>
List super T> e.g List super Number>

T can only be bound to 1 specific type at a time, which is unknown to code inside generics.

List extends Number> means this list holds object of Number or subtype (Float, Integer…) It is safe to read all elements as Number, but it is not safe to put elements Integer or Float into it, because it could be List when you put Float into it.

List super Number> means the list holds object of Number or Object. It is safe to write Number or its subtypes to list, because the list can only be List or List which is safe to read Number|Float as Object or Number

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

void foo(List super Number> list);

what can we do with list inside foo?

A

TLDR: we can only write Integer or Float element into list because the list can only be List or List in either case it is to safe to read Float or Integer as Number or Object

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

void foo(List list);

what can we do with list inside foo?

A

TLDR: we can only read elements as Number because the list can be List or List (and inside foo() we don’t know which)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

wildcard type does not guarantee immutability

A

wildcard is a compile time enforcement

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

[*] Covariant vs Invariant

A

Array of Box Types is Covariant, in that
S extends T then S[] extends T[]

Integer[] and Number[] are covariant
int[] is primitive type array, does not apply

Collection/List is invariant in that
S extends T but List does NOT extend List

Wildcard introduces Covariant to Collection/List
S extends T then List extend List extend T>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

GET/PUT principal applies to Covariant types but violation of the principal is detected at different times (compile time vs run time)

A

List/Collection Covariant:

Get/PUT violation is detected at COMPILE time

Array Covariant:
GET/PUT violation is detected at RUN time (ArrayStoreException)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What is Covariance? (Origin of Array Covariant)

A

TLDR:
Covariance is the “is-a” relationship in inheritance tree.

We know that “Integer is-a Object”

Java, before generics, also defined that
Integer[] is-a Object[]

This allows 
void foo(Object[] a) to be called with foo(new Integer[3])
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q
Unbounded Wildcard 
List>
vs
Bounded wildcard 
List extends T> or  
List super T>
vs 
Bounded Type
List
A

Unbounded wildcard: operations that does not depend on type (such as list.size())
Bounded wildcard : Put or Get but not both
Bounded Type: Put and Get

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What is wrong with this (signage)

public int compareTo(Integer that) {
    // bad implementation -- don't do it this way!
    return this.value - that.value;
}
A

this code may give the wrong answer when there is overflow. For instance, when
comparing a large negative value to a large positive value, the difference may be more
than the largest value that can be stored in an integer, Integer.MAX_VALUE

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Type-Erasure (C++ Code specialization vs Java Code sharing)

A
C++ template uses Code Specialization (1 class generated for each concrete type used with template)
Java generics uses Code sharing (1 class generated for ALL concrete types used with the template)

C++
template
class CList {
}

Java
class JList  {
   T t = new T(); // this is not possible since there is no "Type information" at run time. (Type-erasure)
}

CList and CList are two different classes in C++

JList and JList are same class in Java. To do this java adopt type-erasure to remove all type information inside JList implementation. (I.e. you cannot instantiate objects of T inside JList)

Therefore, In java, Generics and Collection are tightly coupled where generics are usually used with collections where collection operation (len, size, iterate) does not require type info.

If type info is needed, then use Bounded Type extends Number> then you can reference elements as Number.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q
Types of outter class is visible to non-static inner class.
(static nested class are like namespace, thus its type is separated from outter class types)
A

If the outer class has type parameters and the inner class is not static, then type parameters of the outer class are visible within the inner class.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

ERASURE

what is it?

A

(Remember type bound is only done through extends not through “super” and there is no “bounding” done for > )

Erasure is process of enforcing type constraints only at compile time and discarding the element type information at runtime.

Drop all type parameters from parameterized types, and replace any type variable with its bound, or with Object if it has no bound, or with the interfaces if it has multiple bounds

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Multiple bounds: A type T can have multiple bounds
List \ extends C1 & C2 & IF1 & IF2>

Thus the list element must be a subtype of C1&C2 and implements IF1 and IF2.

(C1 &C2 indicates multiple inheritance, BAD DESIGN!)

Java requires Class type precedes interface type

A

In Multiple bounds, usually 1 class and multiple interfaces, compiler erased the left most bound (class bound), and keep the remaining (interface bounds)

So compiler will erase Number and keep comparable.

This the generated class is
Comparable max( Comparable x,...)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Signatures for methods should be as general as possible to maximize utility. If you can
replace a type parameter with a wildcard then you should do so.

A
We can improve the
Signature of max by replacing:
T max(Collection collection)
with:
T max(Collection  \< ? extends T> coll)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Why is System::arraycopy() a native method?

A

In native mode, it allows using pointers to fast access to consecutive objects in continuous memory block (e.g. memcpy()) and other optimizations.

I.e. do a block memcpy() instead of object-wise copy

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

List < T extends Comparable < ? super T> >

A

it means that T can implement Comparable super T>, not just Comparable.

For example, if Student is-a Person
it means that a Student class can implement Comparable, where Student is a subclass of Person:

Usually, Student implements Comparable
and Person implements Comparable
but since Student inherits Person, student inherits Comparable as well

Thus Student satisfies > even if Student itself does not implement Comparable

18
Q

Java Generics does not support primitive types.

A

List asList(T…a);

when you call Arrays.asList(1,2,3)
it is same as Arrays.asList(Integer(1), Integer(2))
Java infers type from argument 1,2,3

Thus compiler sees
List Arrays.asList(Integer(1), Integer(2)..)

19
Q

Because java generics uses Code Sharing (no code expansion or specialization as in C++) a generic template has only 1 class|method|implementation for all types meaning inside the template there is only 1 type “Object” meaning type-erasure at run time

Because of type-erasure, you cannot refers to the type symbol (e.g. “T”) in side the code (because it is removed prior to runtime) that has any run-time behavior (execution|storage etc).

T is valid in declaration (compile-time)
but invalid in definition (implementation, run-time)

A

Thus these sorts of references of type T in code is invalid:

(static data member declaration) static private T t;
new T();
instead of T

(T is pretty much an invalid symbol other than in class or function declarations or type conversion)

20
Q

Java Lambda is a syntax sugar for Anonymous (Inner) class, where a function object is created.

/* anonymous class */

Runnable myTask = new Runnable() {
  // impl
}
/* lambda */
Runnable myTask = () -> {/*impl*/};
A

Anonymous Inner class can only access FINAL variables from outer method.

Because anonymous class is like a callback, and the outer variables used inside there are like callback data, they are not allow to be changed to refer to different object.

Thus the variable needs to be final so it cannot be reassigned. (But the object the variable refers to can be mutable)

21
Q

final vs effective final

A

A variable is final when it is explicitly declared be final.
A variable is effective final when it is only assigned once.
(E.g. c++ reference is effective final)

22
Q

Lambda Expression and Functional Interface

Lambda Expressions are expressions, thus like other expressions, they have types

When Lambda expressions are assigned as function arguments, they have types

E.g.
expression (3+2) is of type int
Runnable myTask = () -> {/impl/};

void foo(Runnable task) {
 /* task is a "Function Pointer" */
}
A

Lambda expression is function object (or C++ function pointer).

Lambda expression implements interface, thus is an function object of that interface.

But 1 lambda expression can only implement 1 method of the interface. Thus interfaces with 1 method is called Functional Interface.

Runnable myTask = () -> {/impl/};

where the lambda expression is a Runnable function object.

23
Q

Raw type of Generics is Object

Prior to Java 5, Collections support only Raw Type (i.e. Object), thus to create a list you do
List cars = new ArraysList();

In java 1.5, generics are introduced thus we do
List cars = new ArraysList<>()

But for backward compatibility the old grammar is still kept
List cars = new ArraysList();

which means
List cars = new ArraysList<>();

A

A raw type is the name of a generic class or interface without any type arguments. For example, given the generic Box class:

public class Box {
    public void set(T t) { /* ... */ }
    // ...
}
To create a parameterized type of Box, you supply an actual type argument for the formal type parameter T:

Box intBox = new Box<>();
If the actual type argument is omitted, you create a raw type of Box:

Box rawBox = new Box();

24
Q

Streams is Functional Programming

Functional Programming: Input->filter->output with filter having no mutable states.

stream operations are composed into a stream pipeline
, a pipeline of “functional filters”.

A

Java Stream pipeline ~=> gstreamer pipeline ~=> mediapipe pipeline (all pipelines are declarative, not imperative programming)

A pipeline of “functional filters” with each filter have no mutable states.

Each filter must be non-interfering (they do not modify the stream source); and in most cases must be stateless (their result should not depend on any state that might change during execution of the stream pipeline).

This matches functional programming paradigms:

Functional methods are stateless. It has no hidden input or hidden output.

All its input are in the function signature.
All its output are in the return value.
No side effects.

25
Q

Stream pipeline is lazy-evaluation until terminal operation which is eager-evaluation

Stream pipeline traverses source only once, regardless how many filters in between.

A

A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)).

Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

26
Q

Stream pipeline is declarative

A

Streams declaratively describing their source and the computational operations (ma) which will be performed in aggregate (reduce) on that source

27
Q

A stream pipeline, can be viewed as a “SQL query” on the stream source (Lazy evaluation)

A

similarities between pipeline and SQL query (lazy evaluation, parallel execution etc)

28
Q

A stream pipeline, can be viewed as a “SQL query” on the stream source (Lazy evaluation)

A

similarities between pipeline and SQL query (lazy evaluation, parallel execution etc)

many operations in stream can be reflected in similar SQL statements
FROM: stream
WHERE: filter
SUM|COUNT|MIN|MAX: sum/count/min/max etc

29
Q

Benefits of lazy-evaluation

E..g

IntStream()->filter(x->x > 10)->count() //need to traverse all elements
IntStream()->filter(x->x > 10)->findFirst() //travers till first x>10

A

we can see that wait till count() or findFirst() can save how many times filter(x->x>10) is executed.

Thus lazy-evaluation waits until knowing what the user wants (which is expressed by terminal operation)

30
Q

Java stream vs Rx.java (ReactiveX) vx Vert.x

A

Streams and Rx.java have Similar APIs (map, flatMap etc) but different purpose

Virt.x is a event-driven web server framework (vs Sprintboot e.g.) (that could be implemented with Rx.java)

31
Q

Java stream vs Rx.java (ReactiveX) vx Vert.x

http://reactivex.io/documentation/operators/flatmap.html

A

Streams and Rx.java have Similar APIs (map, flatMap etc) but different purpose.

Rx.java (ReactiveX) claims Reactive Extensions for the JVM (v.s. Springboot reactive)

Virt.x is a event-driven web server framework (vs Sprintboot e.g.) (that could be implemented with Rx.java)

32
Q

think about whether you really want to expose List
or Set or Array objects in your domain model. Perhaps a Stream factory would be a better choice. The big win of only exposing collections via Stream is that it better encapsulates your domain model’s data structure. It’s impossible for any use of your domain
classes to affect the inner workings of your List or Set simply by exposing a
Stream.

A

I.e. if you only expose Streams from your API (i.e. only take Streams as input and return Streams) then you can further abstract away your implementation details like If you are using linked list or array or set or hashmap etc.

33
Q

Java stream vs Rx.java (ReactiveX) vx Vert.x

http://reactivex.io/documentation/operators/flatmap.html

A

Streams and Rx.java have Similar APIs (map, flatMap etc) but different purpose.

Rx.java (ReactiveX) claims Reactive Extensions for the JVM (v.s. Springboot reactive)

Virt.x is a event-driven web server framework (vs Sprinhboot e.g.) (that could be implemented with Rx.java)

34
Q

IMPORTANT PROS and CONS
think about whether you really want to expose List
or Set or Array objects in your domain model. Perhaps a Stream factory would be a better choice. The big win of only exposing collections via Stream is that it better encapsulates your domain model’s data structure. It’s impossible for any use of your domain
classes to affect the inner workings of your List or Set simply by exposing a
Stream.

A

I.e. if you only expose Streams from your API (i.e. only take Streams as input and return Streams) then you can further abstract away your implementation details like If you are using linked list or array or set or hash map etc.

but returned list or array can be reused to iterate (a new iterator)

returned streams can only be used once. Have to collect the returned stream into list then generate a new stream

35
Q
Why is Stream::map() signature is 
Stream map(Function super T, ? extends R> mapper)
A

Stream map(Function super T, ? extends R> mapper) means mapper can be a function that can convert T or its super class to R.

E.g. 
If T = Integer, R=Float, the signature means you can provide a functor to 'mapper' that can convert Number or Object to Float. Since T = Integer is a subclass of Number or object, the functor can accept T

In terms of generic PUT|GET principle, here the PUT|GET is from caller’s perspective:

when caller calls the mapper its PUTs argument , and GET return value

36
Q

Java generics are based around erasing a generic parameter—in other words, pretending it’s an instance of Object—only the boxed types can be used as generic
arguments.

A

That is why it is List and not List

37
Q

Integer[] vs int[]
Performance!

upto 6x more memory usage

A

boxed types are objects, there is a memory overhead to them. For example, although an int takes 4 bytes of memory, an Integer takes 16 bytes.

This gets even worse when you start to look at arrays of numbers, as each element of a primitive array is just the size of the primitive, while each element of a boxed array is actually an in-memory pointer to another object on the Java heap. In the worst case, this might make an Integer[] take up nearly six times more memory than an int[] of the same size.

There is also a computational overhead when converting from a primitive type to a boxed type, called boxing, and vice versa, called unboxing. For algorithms that perform
lots of numerical operations, the cost of boxing and unboxing combined with the additional memory bandwidth used by allocated boxed objects can make the code significantly slower.

38
Q

Integer[] vs int[]
Performance!

upto 6x more memory usage

LongStream: operates on long primitive
Stream: operates on Long object.

LongStream.of(long[]) == >
LongStream.boxed()
==> Stream

It’s a good idea to use the primitive specialized functions
(LongStream, IntStream, DoubleStream) wherever possible because of the performance benefits.

A

boxed types are objects, there is a memory overhead to them. For example, although an int takes 4 bytes of memory, an Integer takes 16 bytes.

This gets even worse when you start to look at arrays of numbers, as each element of a primitive array is just the size of the primitive, while each element of a boxed array is actually an in-memory pointer to another object on the Java heap. In the worst case, this might make an Integer[] take up nearly six times more memory than an int[] of the same size.

There is also a computational overhead when converting from a primitive type to a boxed type, called boxing, and vice versa, called unboxing. For algorithms that perform
lots of numerical operations, the cost of boxing and unboxing combined with the additional memory bandwidth used by allocated boxed objects can make the code significantly slower.

39
Q

@FunctionalInterface

Lambda Expression are implementation of @FunctionalInterface

All @FunctionalInterface have single abstract method
Not all interfaces with single abstract method are @FunctionalInterface

void Foo(Comparable comp);
void Bar(Closable resource);
Here comp cannot be supplied as a lambda, but have to be done as a anonymous class.
A

@FunctionalInterface

1) has at least input
2) the implementation of the single abstract method is pure function (i.e. does not require using any T’s methods (after all, the implementation has to work for any type of T)

Comparable {
  boolean compareTo(T t);
}

The implementation of compareTo() is not pure function because it need to access T’s state to do comparison.

Closable {
  void close();
}

The implementation of close() is not pure function because it needs to access some external resource to “close”.

Thus the class that implements Comparable or Closable must have internal states, and these classes thus are not pure functors, thus the Comparable and Closable are not mean to be used as Lambda Expression types.

is not @FunctionalInterface

40
Q

Default method in Inteface

A

expand interface without breaking existing implementations of that interface.

41
Q

Because of performance impact of Boxed types, when large data is considered, we should use primitive type.

Thus Java provide many primitive type specific libraries such as IntStream, or Arrays.stream(int[]) (vs Arrays.stream(T[])

Downside: many very similar implementations. (heavy code duplication)

E.g. a “int[]” impl is very similar to a “long[]” impl

E.g. java.util.arrays has comment:

The code for each of the seven primitive types is largely identical. C’est la vie.

Solution is to use code generator.

A

Anything offered by Collections such as Collections.sort have to use boxed types because of generic types must be objects.

Java quicksort (DualPivotQuickSort) supports only primitive types

DualPivotQuickSort::
static void sort(long|int|short|byte|char[] …)

42
Q

Stream “Encounter order”

The “traverse” order offered by the input stream

A

A intermediate operation may alter the encounter order of the output stream.