I see where you're coming from, but I'm personally not inherently opposed to having a unique string identifier that's globally unique across all ids. It makes it easier to reason about them. I can agree that "filename" is not the correct name here, for blocks that are not backed by files, though.

What would you say to making the following changes:
- change filename to asFilename,
- add a protected field "name" which all BlockIds implement instead of filename,
- and make equality check for type sameness

I left out making equality check all the fields as I think this would complicate the code more than its worth, if we assume all identifying keys are simple objects like ints and strings which can be compared easily in string form. (Ultimately, the internal implementation of BlockId can be whatever, so until we get complex keys, I'm not sure field checking has inherent value.)