Salsa and Java Generics: Understanding the Consumer interface
satya - 9/8/2018, 12:33:34 PM
Salsa for two left feet
Salsa instructors think it is math!
They may make you count, albeit with shuffling feet.
For some they just do it.
Any amount of theory is not going to help those feet to get to the feat.
Same with generics.
You can study all day long and yet their signatures stay theoretical.
They stay theoretical because, the language of generics, is really a set of patterns
Like a real language you don't think in grammar! You just have to know the patterns and forget the language.
So what a generic syntax says you have to internalize the pattern behind it!!!
satya - 9/8/2018, 12:34:32 PM
Consider this picture
satya - 9/8/2018, 12:35:14 PM
Lets read this picture now, and then we will move to say this in generics
Lets read this picture now, and then we will move to say this in generics
satya - 9/8/2018, 4:15:10 PM
Lets talk Tigers first: The object that is being treated
The system above is working with objects of type Tiger.
In the hierarchy a Tiger is an animal
A tiger is not a bird
A tiger is a cat
But a tiger is not a Bengal Tiger! Because Bengal Tiger has additional stuff than a regular tiger. A Bengal Tiger is a tiger but a Tiger is NOT a Bengal Tiger.
satya - 9/8/2018, 4:16:40 PM
The Workers
The system contains a number of workers
Each worker specializes in working with a particular type of creature
These workers are not interchangeable
satya - 9/8/2018, 4:18:32 PM
The Supplier
Now, there is a supplier that has a collection of Tigers. It is a Tiger Supplier.
A Tiger supplier wants to "Give Tiger(s) To" a worker so that the tigers can be processed by the worker that is capable of working with that Tiger.
satya - 9/8/2018, 4:24:04 PM
The "GiveItTo()" function
The supplier will call the "GiveItTo(Worker)" function
At this juncture, what should be nature (type) of the worker?
The rule the supplier may want to express is
1. I want to make sure the worker knows how to work with Tigers. Because I am passing a Tiger.
2. It is ok if the worker knows how to work with Cats, or Animals. Because a Tiger is a cat and also an animal. It is fully type substitutable for cats and animals.
3. If the worker knows to work with birds, then that worker should be rejected.
4. the worker should also be rejected if the worker is in the habit of "Bengal Tigers". Because the worker may expect a BengalTiger but what is passed is less than a BengalTiger.
satya - 9/8/2018, 4:25:29 PM
So what are we saying
Please give it to worker that knows how to work with Tigers or workers that knows how to work with any of its base classes!
satya - 9/8/2018, 4:27:30 PM
How do we say this programmatically
void GiveItToWorker(
Worker<? super Tiger> worker);
satya - 9/8/2018, 4:28:04 PM
Consider the code below now
private void test1()
{
Collection<Person> people =
Person.createRoster();
people.stream().forEach((p) -> {
System.out.println("Test1: Person:" + p);
}
);
}
satya - 9/8/2018, 4:34:52 PM
Here is the signature of forEach() method
interface Stream <T>
{
...
void forEach(Consumer<? super T> action);
...
}
//and here is the Consumer
interface Consumer<T>
{
void accept(T t);
...
}
satya - 9/8/2018, 4:40:27 PM
Some example consumers
Consumer<String> stringConsumer;
Consumer<Object> anyObjectConsumer;
Consumer<Person> personConsumer;
Consumer<Male> maleConsumer;
//So the signature
void forEach(Consumer<? super Person> personConumer)
//will only match
anyObjectConsumer
personConsumer
//but will fail
stringConsumer
maleConsumer
satya - 9/8/2018, 4:42:11 PM
What is this? What kind of a consumer is this?
people.stream().forEach((p) -> {
System.out.println("Test1: Person:" + p);
Here the lambda function creates a Consumer
satya - 9/8/2018, 4:52:57 PM
So that argument type again
//A function in java points to only ONE type:
f1(String s)
//You cannot say
f1(String x or MyString y, ...other args)
//But generics allows you to say
f1(Any-Type-Super-class-Of-String flexibleArg)
//The way you say this is
f1(<? super String> flexibleArg)
//However, however this only applies
//when the "ARG" TYPE ALLOWS parameterization
//For example, String does not allow parameterization
//So it needs ot be
f1(SomeClassThatAllows<? super String> flexArg)
satya - 9/8/2018, 5:23:09 PM
Consider a hierarchy
Male : M
Person: P
Object: O
satya - 9/8/2018, 5:24:42 PM
Now consider a function
f(P)
-- allows any method on P
-- allows any method on O
-- DOES NOT allow any method on M
satya - 9/8/2018, 5:26:37 PM
Consider this
f(P)
E - stands for allowed execution
T(E) - stands for some transformed (on P) execution
//what is true in both cases under f(P) is
//so both for E and T(E)
-- allows any method on P
-- allows any method on O
-- DOES NOT allow any method on M
satya - 9/8/2018, 5:29:41 PM
Lets see what happens when pass things to f(P)
//pass M, P or O to f
f(passedInObject M, P, or O)
E - execution works for P
E - execution will work for M
(because Male is a Person)
E - execution fails for O
(because method of P are getting called
satya - 9/8/2018, 5:33:52 PM
what happens when transformed things are passed to f(P)
//pass T(M), T(P), T(O)
T(M) - Transform M in someway
T(P) - Transform P in some way
T(E) - Transform O in some way
//Now the rules are same
f(P)
Allowed executions are
Person methods
Object methods
--Male methods are not allowed
f(passedInObject T(M), T(P), T(O))
T(P) because P can be transformed with Person methods
T(O) O can be transformed with Object methods
T(M) Male methods not allowed
So T(M) fails
satya - 9/8/2018, 5:35:27 PM
When you directly act on an object type
You can pass any sub types.
But you cannot pass super types
Because sub types satisfy the current type and super type methods, which are being used.
satya - 9/8/2018, 5:45:55 PM
When you act on a transformed object type
There is an intermediary transformation
Previously when a super type is passed, we are saying that is not allowed because, we know we are calling methods that the super type doesn't have, the methods of the current type.
But when there is a Transformer that says I only work with "super type" objects, it is guranteeing that only the super type methods are called. Because of this gurantee T(O) is allowed but not O.
Similarly, previously, when sub type is passed, we allowed it because we know we will treat it as current type and will never call its methods.
But when T(M), transformation is in place of the sub type, that transformation will call methods that are not supported on the current type.
So the type substitution reverses!!
satya - 9/8/2018, 6:11:12 PM
there is a lot of confustion
there is a lot of confustion
satya - 9/8/2018, 6:11:19 PM
let me try again
let me try again
satya - 9/8/2018, 6:13:01 PM
Me and My delegated worker, a function says
f(P)
//work P
//call P methods
//call O methods
//can't call M methods
//obvious so far
//D(p) where D is a delegate
f(P)
D(P)
//Notice how D is used
satya - 9/8/2018, 6:15:03 PM
So what is the type nature of D(P)?
D(P)
- call P methods
call O methods
CANNOT call M methods
D(O)
- call O methods only
D(M)
- Calls M, P, O methods
satya - 9/8/2018, 6:17:04 PM
So...
f(P)
D(P)
//is equivalent to
f(
D(P) -- incoming is P.
D(O) -- incoming is P. So O methods ok
D(M) -- Not allowed)
)
//So
f(P) = f[ D(P) or D(O) ]
satya - 9/8/2018, 6:18:22 PM
So ...
f(P) can successfully delegate its functionality to those worker objects that promises that they will work with only P or O objects.
So the type signature of f() and D() is quite different!!!
satya - 9/8/2018, 6:26:23 PM
Another way to read this
f(P) is a way to specify incoming object types.
f[ D(P) or D(O) ] is a way to specify type for What code segments are allowed
So
f(O) //Don't allow. Does not impact D(P) or D(O)
f(P) //allow. D(P) or D(O) knows how to work with P
f(M) //allow. D(P) or D(O) knows how to work with P of M
But if you allow D(M) as well then
f(P) will route a P-person object to D(M) - a male code executor and calls male methods on a person object and that can be wrong.
satya - 9/10/2018, 12:02:28 PM
Recasting types as a receiver and sender
satya - 9/10/2018, 12:03:21 PM
Sending as a call to a function
satya - 9/10/2018, 12:04:10 PM
Function calls as a type descendency
satya - 9/10/2018, 12:06:50 PM
Call summary
You can be called with higher specialized types, but you can call functions only with base types or at your own level of type.