Il y a deux choses à comprendre :
- le type erasure : comme tu l'as dit, ça fait que au niveau bytecode, doIt(Titi<?> ) et doIt(Titi<String> ) ont la même signature
- la signification de la surcharge : quand tu surcharges une méthode (abstraite ou concrète, ça ne change rien), tu dois fournir une méthode qui peut être appelée exactement comme la méthode de base. En particulier, tu ne peux pas mettre des contraintes supplémentaires (par exemple, c'est interdit d'envoyer des checked exceptions qui ne sont pas déclarées dans la méthode de base)
Dans ton cas, la méthode abstraite attend un Titi<?>, et tu cherches à la surcharger avec une méthode qui n'accepte que Titi<String>. C'est une contrainte supplémentaire. Pour s'en convaincre, il suffit de voir que la méthode abstraite peut accepter par exemple un Titi<Integer>, alors que ton implémentation la refusera.