A NICE programozási nyelv a JAVA-hoz hasonlóan erősen típusos nyelv, azaz minden kifejezés és változó típusa egyértelműen meghatározható már fordítási időben. A típusok meghatározzák, hogy az adott változó milyen értékeket vehet fel, illetve milyen kifejezéseket képezhet, milyen műveletek végezhetőek rajta. A NICE, a JAVA-hoz hasonlóan 2 fő típusosztályt különböztet meg, az úgynevezett primitív típusokat, illetve a referenciatípusokat, amelyek az összetett típusok (osztályok) objektumaira való hivatkozást segítik.
A NICE még további megszorításokat is tesz ezen típusokra, mégpedig aszerint, hogy az illető típus felveheti-e a null értéket, vagy sem. Az ilyen megkülönbözetett típusokat “opcionális típusnak” (optional type) hívjuk. Ezeket a nyelv szintaktikusan megkülönbözteti egymástól és a köztük való konverzió csak olyan módon lehetséges, hogy előbb meg kell vizsgálni hogy az aktuális változó értéke null-e vagy sem és ha nem az, akkor konvertálható olyan típussá, amely nem tartalmazhatja a null értéket. Ez a megkülönböztetés a legtöbb hibás (nem inicializált) mutatóhasználat esetén már fordításidejű hibát fog okozni, illetve rákényszeríti a programozót a helyes esetkezelésre.
Egy másik fontos különbség a JAVA típusrendszeréhez képest, hogy a NICE nem engedi meg az explicit típuskonverziókat az osztályokon (class cast), hanem az instanceof operátor segítségével elágaztathatjuk a programunkat, és azon ágon, ahol a fordítóprogram tudja, hogy lehetséges a konverzió, ott azt automatikusan engedélyezi. (Ennek a kezelési szerkezete hasonló a null változók konverziójához, mindkettőre lesz példa a későbbiekben.)
Mindkét fenti módszernél figyelembe kell venni azt is, hogy azok csak lokális változókra (lokális referencia) működnek, hiszen az osztályok tagjai esetén előfordulhat, hogy a vizsgált érték menetközben megváltozik (például egy többszálú program esetén egy másik szál megváltoztathatja az adott objektum értékét). Emiatt az osztály tagjain elvégzett ilyen konverzióhoz előbb egy lokális másolat készítése szükséges.
A NICE ezenkívül rendelkezik a típusparaméterezés lehetőségével is (hasonló módon a C++, illetve a JAVA5.0-hoz), azonban itt további megkötések is megadhatóak a típusparamétereket illetően, például hogy melyik ősosztályból származó objektumok fogadhatóak csak el, illetve megadható olyan reláció is a típusparaméterek között, mint pl. bármely U és T típus megfelel, ameddig U altípusa T-nek.
Ezeken kívül lehetőség van még un. absztrakt interfészek (abstract interface) létrehozására is. Ezek hasonlóak a normális interfacek (a JAVA interfészei, amelyek használhatóak NICE-ban is) működéséhez abban, hogy itt is megadhatóak olyan függvények, eljárások az interface tagjaként, amelyeket később egy osztály megvalósítva az interfészt kifejthet, azonban két fontos különbség is van. Az első az, hogy egy absztrakt interfész esetén lehetőség van arra, hogy azt egy adott már létező osztály valósítsa meg később, anélkül, hogy annak akár a forráskódja elérhető lenne (és módosítani kéne). A másik különbség, hogy egy absztrakt interfész nem egy tényleges típust definiál, sokkal inkább egy jelölést, amely a későbbiekben alkalmazható lesz más típusok kiterjesztéséhez. Ennek segítségével aztán készíthetünk pl. olyan függvényt, amely ezzel az absztakt interfésszel van típusparaméterezve és alkalmazhatóvá válik minden olyan típus esetén, amely megvalósítja az interfészben megadott funkciókat. Ennek segítségével pl. egyszerűen megvalósítható egy “loggolás” művelet, hiszen elegendő egy absztrakt interfészt megadni, egy log metódusra, és aztán ezt a metódust megvalósítani minden loggolható típusban (akár már létezőben is, később kiterjesztve vele az osztályt multi-metódusok használatával, nem szükséges annak eredeti kódját változtatni).
A továbbiakban rövid példák találhatóak az említett kiterjesztések használatára, illetve azok szintaxisára:
Az elemi numerikus típusok a csökkenő ábrázolási tartomány szerinti sorrendben: double, float, long, int, short, byte. A “kisebbtől” a “nagyobb” felé teljesen automatikus a konverzió. Az ellenkező irányban explicit konverzió szükséges, amelynek alakja type(e), ahol a type a céltípus neve, az e pedig a konvertálandó kifejezés. Ez ekvivalens a JAVA-ban használatos “(type) e” kifejezés használatával.
A NICE referencia típusai alapértelmezetten nem tartalmazhatják a null értéket, ezért ha olyan típust szeretnénk megadni, amely felveheti a null értéket, azt a típusban külön jelölni kell: Pl. ha a ‘String’ típusnak szeretnénk megengedni a null értéket, akkor a deklarált változónk elejére a ‘?’ jelet kell illeszteni, azaz a null-al kiegészített típus neve a ‘?String’ lesz. Azokban az esetekben, amikor nem lehet tudni, hogy az eredeti típus megengedi-e a null-t (például egy típusparaméterezett esetben), használható a ‘!’ prefix is, amely tetszőleges típus azon verzióját jelenti, amely nem engedi meg a null-t. (Például ha a ’T’ típus a ’String’, akkor ’!T’ is a ’String’, ha ’T’ a ’?String’, akkor ’!T’ a ’String’.)
Példa opcionális típus használatára:
Mivel a klasszikus típuskonverzió nem létezik NICE-ban osztályokon, ezért hasonló hatást csak fenti példához hasonlóan tudunk elérni. Például, ha A osztály és B gyermeke A-nak, akkor A-ból B-be való konverziót csak az alábbi módon érhetünk el:
Az absztrakt interfészek használatával már meglévő osztályainkat bővíthetjük új, közös funkcionalitással. Ennek előnyeit legjobban az alábbi példán keresztül lehet szemléltetni, amely a naplózás műveletével bővíti ki a már meglévő osztályokat:
A NICE nyelv lehetővé teszi az adatok rendezett csoportokba (n-esekbe) rendezését, amelyek segítségével bizonyos műveletek könnyebben és átláthatóbban írhatóak le, logikailag és szintaktikusan összefűzhetnek több adattípusból álló elemeket is, lehetővé téve egyfajta „anonim struktúrák” használatát például a paraméterátadásoknál, amikor egynél több paramétert szeretnénk visszaadni egy függvény visszatérési értékeként. Ezenkívül érdekességképp meg lehet említeni, hogy ez a struktúra lehetővé teszi pl. két változó értékének felcserélését is, ideiglenes változó bevezetése nélkül.
Változók értékének felcserélése:
Egy függvény visszatérési értéke is lehet rendezett n-es, így több visszatérési érték átadása sem lesz nehézkes.
A tuple-k megadása sima zárójelek között, vesszővel elválasztott típusnevek
felsorolásával történik, és inicializálásuk is ilyen módon érhető el.
Pl: (int, int) x = (3,4);
A paraméterben kapott tuple értéke is felbontható lokális változókra, ahogy a következő példában is látható.
Ez a funkcionális nyelvek mintaillesztéséhez hasonlítható technika.