There is EVERYTHING wrong with both of them both design-wise and implementation wise. A string is an array of characters but I don't see you storing your strings as byte arrays. By your logic - it works so there is no harm in it, right? Even if it works it makes no sense logically - you are not going to use ByteBuffers for your numbers and you are thus not going to use a CommonProxy, the string is not annotated as Iterable<Byte> and your items should not be annotated with IHasModel. It also confuses people all around the world who look at your code - there is no Common side, so why is there a common proxy then? And some of them start using common proxy to execute code they consider common which is plain wrong and has lead to numerous issues, some of which I have helped people with on these very forums, so it IS HARMFUL! Don't just shrug it off because it works. If something works it doesn't mean it's right.
If you know what you are doing then by themselves these classes don't do harm. It's a whole other story if you don't though. And a lot of people don't.
You are still registering them individually though. Just this time your code is in your Item class instead of your event class. It is also in your proxy. You are actually legitemately writing 300% MORE code by using IHasModel. It also makes no sense design-wise since all items need models. A string isn't IHasCharacters for a reason. Again, this HAS caused issues some of which I had to help people with so it IS ACTIVELY HARMFUL.
And in any case you can iterate your items just fine since they already share a common ancestor - Item! So the argument of "but I can iterate them now" is also invalid.
It can't though. Proxies by definition contain code that will crash the game if ran on the wrong side. You can't share code between server and the client like this, it happens anywhere else but your proxy. Again, there were numerous issues caused by people trying to do exactly that.
Again, if something works doesn't mean it's right and you can't say "code preference" either. You can use strings to store any information but you are not doing that and it's not just "code preference" even though it works.
Yes you do, actually. There are multiple reasons to use object holders, the main of which is the fact that stuff in registries CAN BE OVERRIDDEN! If you don't use object holders you now have a useless thing that is not used in the game at all.
No, they really don't. The main reason is that they offer no control over when the stuff initializes, so one wrong mention of a class that contains them and now you have nulls everywhere because they reference things that have not been instantinated yet. Actually around 20-30% of issues I have seen on this forum related to blocks/items have been caused by people using static initializers! It is actively harmfull! Additionally if that wasn't enough on it's own it breaks the whole "registries can override stuff" concept and it prevents reloadable registries.
Worse, similar to bytecode editing it can and WILL break unpredictably when you least expect it regardless of "complexity". It already had for multiple people who then went to this forum.
So please, don't encourage cargo-cult programming and spread misinformation. There is a reason we tell everyone not to use these things beyound simple design issues. There are guidelines to using forge for a reason. Even if you yourself know how everything works you are actively confusing people who don't and are creating more issues in the process. Imagine if opengl had no guidelines and people started doing stuff like IHasVertex for their buffers, or CommonShader that holds the "common shared" shader code, or god forbid used static initializers to instantinate VBOs and other stuff *shrugs*.