In Flink, windows and keys are largely independent of each other. Stream elements can be grouped by key, and by window, and these are orthogonal dimensions. (When we want to talk about the combination of a window with a key this is called a pane.)
Window instances don't have keys, and neither do window assigners. Instead, keys and key-partitioned state are part of the runtime context in which windows are evaluated.
When I was trying to understand the relationship of keys to window assigners I found it helpful to read through the WindowOperator's implementation of processElement. This code is called as each stream element arrives at a window operator. Paying attention to the role of the key, while leaving out a lot of other details, we see this:
public void processElement(StreamRecord<IN> element) throws Exception {
final Collection<W> elementWindows = windowAssigner.assignWindows(
element.getValue(), element.getTimestamp(), windowAssignerContext);
...
final K key = this.<K>getKeyedStateBackend().getCurrentKey();
...
for (W window: elementWindows) {
...
windowState.add(element.getValue());
triggerContext.key = key;
triggerContext.window = window;
TriggerResult triggerResult = triggerContext.onElement(element);
if (triggerResult.isFire()) {
...
emitWindowContents(window, contents);
}
...
}
}
Here you can see that the key is available to the window operator via getKeyedStateBackend(), but isn't even retrieved until after getting the windows for this element from the window assigner. The window assigner does its job without any concern for keys.
The key is later being fetched, though, so that it can be made available to the trigger via the trigger context.