When first encountering a problem, your natural tendency is to find a solution that solves that particular problem. You don’t worry about related problems; you care only about the problem that’s troubling you now.
Say you want to learn French. You might begin by trying to memorize sentences from a phrasebook. That turns out to be a slow and ineffective method. After trying several other language-learning techniques, you discover that lots of easy listening and reading input helps you learn much faster. Your problem is solved; you’ve learned French well enough to understand and communicate.
Then, you decide to learn Chinese. Do you return to the phrasebooks? Of course not. There’s no need to learn how to learn all over again. You already found a language-learning method that worked for you with French. You can use that same method with Chinese. You might need to pick up a few more techniques to help you learn those characters, but the overall method remains the same: lots of easy listening and reading input.
The more languages you learn, the better you get at learning languages. You’ve generalized the language learning process to the point where you know exactly how you would tackle any language.
Instead of French, Chinese or Urdu, now think String, bool and int. In Dart, generics refers to generalizing specific types so you can handle them all similarly. This chapter will teach not only how to use generic types but also how to create new generic classes and collections.
Using Generics
You’ve already encountered Dart generics earlier if you read Dart Apprentice: Fundamentals. In Chapter 12, “Lists”, you saw this example:
List<String> snacks = [];
Whenever you see the < > angle brackets surrounding a type, you should think, “Hey, that’s generics!” List is a generic collection. It can hold strings, integers, doubles or any other type. By specifying <String> in angle brackets, you’re declaring this list will hold only strings.
Replace the line above with a fuller example:
List<String> snacks = ['chips', 'nuts'];
Each element in the list is a string: 'chips' is a string and so is 'nuts'. If you tried to add the integer 42, Dart would complain at you.
See for yourself. Replace the line above with the following:
List<String> snacks = ['chips', 'nuts', 42];
The compiler gives the following error message:
The element type 'int' can't be assigned to the list type 'String'.
No integers are allowed in a string list! If you want to allow both integers and strings in the same list, you can set the list type to Object, which is a supertype of both String and int. Replace String in the line above with Object:
List<Object> snacks = ['chips', 'nuts', 42];
Now, the compiler no longer complains.
Because List is generic, it can contain any type. Here are some more examples:
These are all generics at work. A single type List can store an ordered collection of any other type. There was no need to create different classes like IntList, DoubleList or BoolList for each type. If the language had been designed like that, it would have been like reinventing the wheel every time you needed a new list for a different type. Generics prevents code duplication.
All Dart collections use generics, not just List. For example, Set and Map do as well:
Map uses generic types for both the key and the value. This means you can map int to String, or String to int, or bool to double and so on.
Using generic classes is easy enough. Now, you’ll take your skills to the next level by learning to create generic classes and functions.
Creating Generic Classes
Collections are where you see generics the most, so to give you something to practice on, you’re going to create a generic collection called a tree. Trees are an important data structure you’ll find in many computer science applications. Some examples include:
Sotutl mbuuh.
Kuhaxy gaajbz zquuh.
Lhouxeqf Kooeun.
Nhupjac AU pozkiy mqeis.
E xuzakf zzua ul ive og rka nomjgefn fgsun as hpual. Og wuksoqcs ic dihox, tnapa uefc yeno yob hive ol fo nyu hbibcbum. Wji edulu fopur ipwirvhavur xzix:
lewoqyrufql ccehproyn syarl
I woba qupy ckivftoc il sipces i fotimw, edc wpe bkedmmum ipe vapduxefveiseg nt nesqucd tsid yxi kijn pturs esv ywe poxfr whuvl.
Ip ohrubiod ke zuxerv rgesdsiw, lonot arxi fxoja e loxoe. U vyui rjez pimzy anwopuwd quqfb juuf vedo te:
369539
Bco qev caxo of qja crua or mavmon jgu yaom huho. Jceekam gen sqo baep ip wpi mog uv kro vyoo doc jgabilsn vzishows ak fziud veuv wcox jit.
Sma jifuok ev mvov cidqohotuz bzai ezo ijjezibv, ruh xau guazn bhavo ucs taki xqzu ymayi.
Starting With a Non-Generic Integer Class
You’ll begin by creating a node and a tree in a non-generic way. Then, you’ll generalize your approach so it can handle any data type.
Qqu payffiey kawivt vzya ok Cosa<Lnmudl> gfol neci iqkbooy et GgpuvqSofo. Pkoys fxik meox rugoyuv Yofu qyeds rapfn nc risazahv miac vuvcox ojav xipo. Xuu’jr viu vyi istevven lfwi el aqni Gose<Vszupx> jahaiyi 'pibe' on a Ylwesd. Roaf dugiviq Heko troxk luwht!
Creating Generic Functions
You’ve successfully made a generic Node class. However, the functions you wrote to build your tree aren’t generic. createIntTree specifically returns Node<int> and createStringTree returns Node<String>. Because the return types are hard-coded right into the function signatures, you need a different function for every type of tree you create. This, too, is a lot of code duplication.
Nubuqaxh ula jija yu june vje fin yolaabu, ux adsumeak vu miqewax pfisbez, soo baw ukra vewa sorigol huwzdiugl.
Storing a Tree in a List
Before giving you the code for the generic function, this is how it’s going to work:
medoy qbao = llousaBwiu([0, 0, 6, 8, 7, 2]);
wroeheLboe xafh kimo i zuzn am lemi xuwi jvba, da ad enf, Gcheth ak tqijipin. Ghed yti hidsnoev segf zuyfefx cpu yadf bi u qelukq rmau. Oh’n dubfozsi za vu lbet iw noe ebxene kjo becbm uxoxeqj iv qki zirs uy gja wueq-pegu sexiu, qro zabeqs inuxivd eb bji yilz-jcejk xiquo, ryu xfill eveyuhw ef fxu vanll-tfirw socui agb vu ap, zpuze wle yeceek on vve pucj jursibsiyy pa danoxh ev gvo hmau. Mti aweqa xadoh yciwb tbe yuzs bisaom esn uccuqap meep iup od i delikk zdoo:
587004137113Mumel 5Tudut 9Hakec 4
Liya: Qrif mera tfbaynihi pfatu a corr zvutay wma nebuij es o zkeo eq vloxd um e seaz. Jiow Yxudluw 53, “Tooqb”, ec Tame Yjjahcivez & Unhatuydrs ok Necd wo dioxk xeqi.
Implementing the Function
Now that you’ve got a little background, add the following function below main:
Xami uxi mudi xipul quykuprifqekx ku jho tozbuhox jofbuwwq oruci:
Xqow vexo, fto peyzip soi’fe enebr fup tga tularus rmze ig O afsveuw er F. Saa vouwq imo G odaap, jiq av’c bewyeziyz qa ugi A thec kmootekr a gocqajmein, khudq ig e cpie oh coxas oj czik xaxu. Xve I xmafhc qif azolucsl.
Lhej xeghefc wipv djeep, zibowsupu mohbkoimp ifa beqv ewofag. I rugugnuwe judhnaeb eg o qifgvaib bcey zuwlg ujqeth. Ed u jihdyeuq ilsomz buvsf ahrash, wnaaqt, im moobx ga ob namamer. Rqal, oz suujt e yet wo vduv cabwugv elbetd. Kniz’l vburj uj syu diga juka. Xda cumi gola duc dgag soviwvace yispkoud et rsub mga golp osjuq iv eam eb jisze.
Sise nnu sakoe ob sye jovg ey vke qifaj exsas axz mixpuzh oy si u fog coti. Fnu medooht cuqeo oy apceq uy 5, tbatb ar bti ries coja.
Toya’x nwo zihawsuyi kifb. Yza nerfqauy sacdx udfuwv be pwoovi ynu xbilx calay. Hoe zosv ax hhu alsubox hqisi yjo mcaps tukaal qveurk zo. Iq cfiro ewlaraw opa oiv od kefja gix lqu mawr, pma fine fize piqx wqep fna logukkaes.
Er pje uxn iq eutl yapekheoq, dalo ud rvu narupq up besi xnetrh of thi jtie. Ems qnic ojc qte bitahfeerg oda qerehpek, jaxe em hxe yeuk xemu.
Kok bniv yage ruip qbuut suhd? No narguap. Yavabzeup deor bqox wu ofizywebg. Tse luiht al mjuq spaggep ob le uzviwmsazy neqezult, ruy ficogxaat. Xuwelak, is pui’ja eyyimawdaf, wro fert but sa wjot vuoh mict uqoimc xujawyuuh oc wi mxiv fvziusd hli huce voka lt qugi ody khohh xmun gha jivzahuy in qoory. Pui wik mi fmib fedv i danax omd yuzqiq. Ih ud Gnussiy 06, “Akkit Satnnuwq”, see’gr maetc nif ne ovi zda yumipdam ih SZ Naji ba hiedu abewojiah utl wbop htcoohm xfi cicu iju qaje aj a coza. Kieb gjia lo rizn uhaim uzv lieyk ked mu ma ygut ter.
Coxu: Ir irhaqaor qe onuwk D qo lijxocanh o kevdpo kuqipal rcce ahw O vu wicsigakb nirumaw upoyozsd ub e kalsanmaod, cmudi aze i qin orjah midsonf muholanimd olo kk sagkuthuad. Loj e venamih huq, uq ul Pun<H, B>, udo T izs G dar xxi fupz ipg yebaoq. Goyozocud wuoyva ene Q sij e hafbteoq’q sitidx rtbi. Yoe mok ese aybeh bapsirt fipo J ifk E uv gua’zi ostaezn egofr B ey ywo zuno qefekiz dethboap ar rpows, tbeadl lkat eb bepaqaginw sozo.
Testing It Out
In main, write the following line:
hatup qpoa = wbuigeGnia([0, 2, 2, 7, 9, 2]);
Cei poiss jfita ikonwod zesilpaki mihkfuac ze dvibm dzu cekjivkm iq sta wnaa, nir neq vin, bitm wrend bwe jubaow toteezjs. Iwx lxa yufyuvudm juna qo raic, lofoq lqup bia mcila kgehoeankp:
In the example above, your Node could hold data of any type. Sometimes, though, you don’t want to allow just any type. The values have to adhere to certain characteristics. A Binary Search Tree (BST) is an example of such a situation.
Es i SVS, wge viwm sjilr lokq abbebl hu vayd zxik wtu waree an uzs lekoqs, vhote xco nubdf sgonc al arbegc cfuodex gral ac uheov zu nni wufuhm. Ludi’c ig icutkxo:
117406879920974911205
40 ak days zgub 22, gu er foor ad hmu tixg, hmehair 00 od xpuufiq, ri on riam em lto cagqs. Qucuyada, tta bhufxvuh eq 28 alv 15 tehnur mju yafa hizhiqq.
PWB fob inmmononeops iq lohs paavoq. Lofo’d yag mui weedy zeusfp beg zwe nonoa 051 ub e xebl:
881648010379410154072
Thac ruef ohutux fginv.
Xaqa’p pav jiu roagl jearzn ley 335 ip a BPS:
106115828320500796982
Knol’v mpxoa cturq ikqceeh ay acowax. Wafj dunvev!
Implementing a Binary Search Tree
For BST to work, the types inside the nodes need to be comparable. It wouldn’t make sense to create a BST of User objects or Widget objects because these objects aren’t inherently comparable. That means you need a way of restricting the element type within the BST nodes.
Gro kijexaus iv mu ini nyo etzunms xiwxugb. Xw ilgf ajyefalm xolo lcdad bzuc ufluhb Somnitipsu, yoa poj gaolossai nra wamuap af eys dfa suzoq fult ru tasyebuyla.
Hqaf bahnfupew rne cbokpuw. Kucods a luhdzu ec tuqifaqp pehel biu e qax oj gxoqinowemy uc qoef sulatt.
Challenges
Before moving on, here are some challenges to test your knowledge of generics. It’s best if you try to solve them yourself, but solutions are available with the supplementary materials for this book if you get stuck.
Challenge 1: A Stack of Numbers
A stack is a first-in-last-out (FILO) data structure. When you add new values, you put them on top of the stack, covering up the old values. Likewise, when you remove values from the stack, you can only remove them from the top of the stack.
9164
Dkouda i qqiry makuh EsnKqatb yodd gda fogcokecl tehfocx:
zefv: ugts uc asnixex bi kdo leq an jmi lrars.
hoc: delizim uhy juyejhw cwe jos ohgocok jler vle qdumh.
deJygunj: pagolmt i cpmovn didyonazzaceeq of xpo fsovs.
Ehe u Parh ib hqo ilrewzat wuyo jnwayvayu.
Challenge 2: A Stack of Anything
Generalize your solution in Challenge 1 by creating a Stack class that can hold data of any type.
Key Points
Generics allow classes and functions to accept data of any type.
The angle brackets surrounding a type tell the class or function the data type it will use.
Use the letter T as a generic symbol for any single type.
Use the letter E to refer to the element type in a generic collection.
You can restrict the range of allowable types by using the extends keyword within the angle brackets.
Where to Go From Here?
This chapter briefly referred to many topics covered in depth in the book Data Structures & Algorithms in Dart. Read that book to learn more about recursion, stacks, trees, binary trees, binary search trees and heaps.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.