skip to content

Across platforms that differ in what they keep of a type argument, what stays observable at run time everywhere?

level: seniorimportance: nice to knowfreq 30%

answer

  1. platforms disagree here
  2. three behaviours, one common floor
  3. the erased shape is the floor
  4. a description sits on declarations
  5. values answer only where kept in full

basics

~20 s

Only the erased shape of a value. Beyond that the platforms split three ways: the argument discarded, kept in full on the value, or kept only as a description attached to a declaration — and a description never makes a value self-describing.

solid answer

~50 s

Only the **erased shape** travels everywhere. Past that, platforms split three ways: some discard the argument outright, some keep it in full so the value itself carries it, and some keep only a **description** recorded against declarations. The third is the one that causes confusion, because a description is real and readable and still does not make a value self-describing: it sits on a field, a method signature or a supertype clause, and says what *that declaration* was written with, not what any particular object is. So a design that asks a declaration what it promised can be made to work on two of the three; a design that asks a value what it was built over works only where arguments are kept in full. The portable floor is the erased shape, and anything above it is a platform commitment worth stating out loud.

go deeper

for a junior

The takeaway is that this is not universal: some platforms drop the argument, some keep it, and the safe assumption when you do not know is that a value tells you nothing.

for a middle

Be able to state all three behaviours and the difference between a record kept on a declaration and one kept on a value.

for a senior

Show that you design to the portable floor: no mechanism whose correctness depends on interrogating a value, and an explicit statement of which behaviour your component assumes.

for a principal

The judgment to own is where to spend portability: committing a shared library to a platform that keeps arguments buys expressiveness and costs you every target that does not, and that bet should be written down, not implied.

## Three behaviours, not two Engineers usually carry a binary in their head — arguments are either erased or they are not — and the binary is too coarse to design against. There are three observable behaviours: 1. **Discarded.** The argument is used to check the program and then dropped. Nothing about it is recorded in the artifact. 2. **Kept in full.** The argument accompanies the value at run time. Two containers built over different arguments are genuinely distinguishable values. 3. **Kept as a description.** The executable shape is erased exactly as in case 1, but the artifact additionally records, against declarations, the signature that was written. Case 3 is the one that produces confident wrong answers, because a reader who has seen those descriptions concludes the platform "keeps generics at run time", and then writes a design that asks a value a question the platform cannot answer. ## The axis that actually matters: declaration versus instance A description attaches to a **declaration site**: - a field declared as *a batch of refund requests*; - a method signature's parameter and return positions; - a clause saying this type extends a parameterized supertype with particular arguments; - the declaration of the placeholder itself, with its limits. It does not attach to an **instance**. The batch object that field refers to is shaped like every other batch. So the question you can answer on such a platform is "what was this declaration written with?", and the question you cannot answer is "what was this object built over?" — even though a careless reading of the same metadata seems to promise both. | Platform behaviour | Ask a value its argument | Ask a declaration its argument | |---|---|---| | Discards the argument | No | No | | Keeps the argument in full | Yes | Yes | | Keeps only a description | No | Yes | ## What this means when you are designing for more than one target The portable floor is the erased shape of a value, and that is all. Three consequences follow for a library that must behave the same on every target: - **Do not build a mechanism whose correctness depends on asking a value what it was built over.** That capability exists on exactly one of the three behaviours, and the failure on the others is not a compile error — it is a design that silently cannot distinguish two cases. - **A declaration-level description is a stronger foundation than a value-level one**, because two of the three behaviours support it — but state the assumption explicitly, because the first behaviour supports neither. - **Say in your own documentation which of the three you assume.** "Works with generics at run time" is exactly the sentence that hides the difference between case 2 and case 3. ## The examiner's view If you actually open the compiled artifact of an order pipeline on each of the three, here is what differs. On the discarding platform, the handler's signature names the collapsed shape and there is nothing else to find. On the description-keeping platform you find the same collapsed signature plus a written record beside it saying what was declared — the erased shape is still what executes. On the argument-keeping platform, the values themselves differ, and the question can be asked of any of them directly. Notice that the *executable* shape is identical in the first two. Whatever is recorded in case 3 is inert with respect to execution: the instructions that run are the same instructions, including the conversions the compiler inserted on the caller's side. The record is documentation the artifact happens to carry, not a change in what the program does. ## Where candidates go wrong The telling mistake is treating a declaration's description as if it were readable *from* an object. The give-away phrasing is "the argument is still there, you just have to look it up" — true of a declaration, false of a value, and the gap between those two swallows a lot of designs. The second mistake is the opposite over-correction: assuming that because one platform discards the argument, no platform anywhere retains anything, and therefore refusing to use information that is genuinely available on a target that records it. The balanced answer names all three behaviours, states the portable floor, and separates the declaration question from the value question without naming any platform at all.

  • Where a platform keeps arguments in full, what changes for someone examining a running program?
    The value becomes able to answer for itself. Two containers built over different arguments are distinguishable objects rather than identically shaped ones, so a design may branch on the argument at run time — which is exactly the design that silently fails on the other two behaviours.
  • Can a recorded description tell you which argument a particular value was built over?
    Only indirectly, and only if you already know which declaration produced that value. The description states what a field or signature was written with; it says nothing about the object itself, so two values reached through different declarations cannot be told apart by it.
  • Does a recorded description change what the program executes?
    No. The executable shape is the erased one either way, including the conversions the compiler placed on the caller's side. The record is extra information the artifact carries about its declarations; the instruction stream is the same as on a platform that records nothing.

saying these in an interview costs you the question

  • Claims every platform keeps some record on the value itself
  • Treats a declaration's description as readable from any object
  • Says a recorded description makes a value's argument observable
  • Assumes all platforms behave identically once compilation finishes
  • Believes an empty container still reveals its declared argument
  • Collapses the three behaviours into an erased-or-not binary