Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
ÉîÈëdz³ö Kotlin Э³Ì
 
×÷ÕߣºEamonn Boyle, Garth Gilmour

  2622  次浏览      30
2020-2-12
 
±à¼­ÍƼö:
±¾ÎĴӵײ㿪ʼ½éÉÜЭ³Ì,ÈçºÎ½¨Á¢ÔÚ°²È«ÓÐЧµØ¶Ô RESTful ·þÎñ½øÐжà´Îµ÷ÓõĹ淶ÐÔ£¬Ï£ÍûÄܶÔÄúÓÐËù°ïÖú¡£
±¾ÎÄÀ´×ÔÓÚInfoQ£¬ÓÉ»ðÁú¹ûÈí¼þAlice±à¼­¡¢ÍƼö¡£

±¾ÎÄÒªµã

JVM ²¢Ã»ÓÐÌṩ¶ÔЭ³ÌµÄÔ­ÉúÖ§³Ö

Kotlin ÔÚ±àÒëÆ÷ÖÐʵÏÖЭ³ÌÊÇͨ¹ý½«Æäת»»ÎªÒ»¸ö״̬»úʵÏÖµÄ

Kotlin ΪʵÏÖʹÓÃÁËÒ»¸ö¹Ø¼ü×Ö£¬ÆäÓà¶¼ÊÇͨ¹ý¿âÀ´Íê³ÉµÄ

Kotlin ʹÓÃÁ¬Ðø´«µÝ·ç¸ñ£¨Continuation Passing Style£¬CPS£©À´ÊµÏÖЭ³Ì

Э³Ì»áʹÓõ½ Dispatcher£¬ËùÒÔÔÚ JavaFX¡¢Android¡¢Swing µÈ³¡¾°Ï£¬Ó÷¨ÂÔÓвîÒì¡£

¾¡¹ÜЭ³Ì²¢²»ÊÇÒ»¸öÐµĻ°Ì⣬µ«ËüÊÇÒ»¸öºÜÎüÒýÈ˵ϰÌâ¡£ÕýÈçÆäËûµØ·½µÄÎĵµËùÊö£¬Ð­³ÌÔÚÕâЩÄêÀï±»ÖØÐ·¢ÏÖÁ˺ܶà´Î£¬ÌرðÊǵ±ÐèҪijÖÖÐÎʽµÄÇáÁ¿¼¶Ïß³ÌºÍ / »òѰÕÒ¡°»Øµ÷µØÓü¡±µÄ½â¾ö·½°¸Ê±¡£

×î½ü£¬Ð­³ÌÒѾ­³ÉΪ JVM ÉÏ·´Ó¦Ê½±à³ÌµÄÒ»¸öÁ÷ÐеÄÌæ´ú·½°¸¡£Ïñ RxJava »ò Reactor ÏîÄ¿ÕâÑùµÄ¿ò¼ÜΪ¿Í»§¶ËÌṩÁËÒ»ÖÖ½¥½øÊ½´¦Àí´«ÈëÐÅÏ¢µÄ·½Ê½£¬²¢¶Ô½ÚÁ÷ºÍ²¢ÐÐÌṩÁ˹㷺µÄÖ§³Ö¡£µ«ÊÇ£¬ÎÒÃDZØÐëÎ§ÈÆ·´Ó¦Á÷µÄº¯Êýʽ²Ù×÷À´Öع¹´úÂ룬ÔÚÐí¶àÇé¿öÏ£¬Õâô×öµÄ³É±¾´óÓÚÊÕÒæ¡£

¾ÙÀýÀ´½²£¬ÕâÊÇ Android ÉçÇøÐèÒª¸ü¼òµ¥Ìæ´ú·½°¸µÄÔ­Òò¡£Kotlin ÓïÑÔÒýÈëЭ³Ì×÷ΪʵÑéÐÔµÄÌØÐÔÒÔÂú×ã¸ÃÐèÇó£¬ÔÚһЩ¸Ä½øÖ®ºó£¬ËüÃdzÉΪ¸ÃÓïÑÔ 1.3 °æ±¾µÄ¹Ù·½ÌØÐÔ¡£Kotlin Э³ÌµÄÓ¦ÓÃÒѾ­´Ó UI ¿ª·¢À©Õ¹µ½·þÎñÆ÷¶Ë¿ò¼Ü (Èç Spring 5 ÖÐÌí¼ÓµÄÖ§³Ö)£¬ÉõÖÁ°üÀ¨Ïñ Arrow(ͨ¹ý Arrow Fx ) ÕâÑùµÄº¯Êýʽ¿ò¼Ü¡£

Àí½âЭ³ÌµÄÌôÕ½

ÁîÈËÒź¶µÄÊÇ£¬Àí½âЭ³Ì²¢²»ÊÇÒ»¼þÈÝÒ×µÄÈÎÎñ¡£¾¡¹ÜÓÐÐí¶à Kotlin ר¼ÒËù×öµÄ¹ØÓÚЭ³ÌµÄÑݽ²£¬ÆäÖкܶ඼ÊÇÆô·¢ÐԵIJ¢ÇÒ°üº¬·Ç³£¶àµÄÐÅÏ¢£¬µ«ÊÇÏëÒª¼òµ¥µØÖªµÀЭ³ÌÊÇʲô£¨»òÕßËü¸ÃÔõÑùʹÓ㩲¢²»ÈÝÒס£Äã¿ÉÄÜ»á˵Э³Ì¾ÍÊDz¢Ðбà³ÌµÄµÈ¼ÛÆ·¡£

µ¼Ö¸ÃÎÊÌâµÄ²¿·ÖÔ­ÒòÔÚÓڵײãʵÏÖ¡£ÔÚ Kotlin Э³ÌÖУ¬±àÒëÆ÷ֻʵÏÖÁËÒ»¸ösuspend¹Ø¼ü×Ö£¬ÆäËûµÄÊÂÇé¶¼ÊÇÓÉЭ³Ì¿â´¦ÀíµÄ¡£Òò´Ë£¬Kotlin Э³Ì·Ç³£Ç¿´óºÍÁé»î£¬µ«ÔڽṹÉÏÈ´²»ÄÇô¹Ì¶¨¡£¶ÔÓÚ³õѧÕßÀ´Ëµ£¬ÕâÊÇÒ»¸öѧϰµÄÕϰ­£¬ÒòΪËûÃÇѧϰµÄ×îºÃ·½·¨ÊÇ×ñÑ­¼áʵµÄÖ¸µ¼·½ÕëºÍÑϸñµÄÔ­Ôò¡£±¾ÎÄ»á´Óµ×²ã¿ªÊ¼½éÉÜЭ³Ì£¬Ï£ÍûÄܹ»Îª¶ÁÕßÌṩÕâÑùµÄ»ù´¡ÖªÊ¶¡£

ÎÒÃǵÄʾÀýÓ¦Ó㨷þÎñÆ÷¶Ë£©

ÎÒÃǵÄÓ¦ÓóÌÐò½«½¨Á¢ÔÚ°²È«ÓÐЧµØ¶Ô RESTful ·þÎñ½øÐжà´Îµ÷ÓõĹ淶ÐÔ£¨canonical£©ÎÊÌâÖ®ÉÏ¡£ÎÒÃǽ«²¥·Å Where¡¯s Waldo µÄÒ»¸öÎı¾°æ±¾¡ª¡ªÔÚÕâ¸ö°æ±¾ÖУ¬Óû§×ñѭһϵÁеÄÃû³Æ£¬Ö±µ½ËûÃÇÕÒµ½¡°Waldo¡±¡£

ÏÂÃæÊÇÍêÕûµÄ RESTful ·þÎñ£¬ËüÊÇʹÓà Http4k ±àдµÄ¡£Http4k ÊÇÓÉ Marius Eriksen ׫дµÄÖøÃûÂÛÎÄÖÐÃèÊöµÄº¯Êýʽ·þÎñÆ÷¼Ü¹¹µÄKotlin °æ±¾¡£¸ÃʵÏÖ´æÔÚÐí¶àÆäËûÓïÑԵİ汾£¬°üÀ¨Scala£¨ Http4s £©ºÍ Java 8 »ò¸ü¸ß°æ±¾£¨ Http4j £©¡£

ÕâÀïÖ»ÓÐÒ»¸ö¶Ëµã£¬Ëüͨ¹ý Map ʵÏÖÁËÒ»¸öÃû×ÖµÄÁ´¡£¸ø¶¨Ò»¸öÃû×Ö£¬ÎÒÃÇҪôÒÔ 200 ״̬Âë·µ»ØÆ¥ÅäµÄÖµ£¬ÒªÃ´ÒÔ 404 ״̬Âë·µ»ØÒ»Ìõ´íÎóÐÅÏ¢¡£

fun main() {
val names = mapOf(
"Jane" to "Dave",
"Dave" to "Mary",
"Mary" to "Pete",
"Pete" to "Lucy",
"Lucy" to "Waldo"
)
val lookupName = { request: Request ->
val name = request.path("name")
val headers = listOf("Content-Type" to "text/plain")
val result = names[name]
if (result != null) {
Response(OK)
.headers(headers)
.body(result)
} else {
Response(NOT_FOUND)
.headers(headers)
.body("No match for $name")
}
}
routes(
"/wheresWaldo" bind routes(
"/{name:.*}" bind Method.GET to lookupName
)
).asServer(Netty(8080))
.start()
}

ʵ¼ÊÉÏ£¬ÎÒÃÇÏ£Íû¿Í»§¶Ë·¢ËÍÈçϵÄÇëÇóÁ´:

ÎÒÃǵÄʾÀýÓ¦Ó㨿ͻ§¶Ë£©

ÎÒÃǵĿͻ§¶ËÓ¦Óý«»á»ùÓÚ JavaFX ¿âÀ´´´½¨Óû§½çÃæ¡£µ«ÊÇ£¬ÎªÁ˼ò»¯ÎÒÃǵÄÈÎÎñ²¢±ÜÃâ²»±ØÒªµÄϸ½Ú£¬ÎÒÃǽ«»áʹÓà TornadoFX £¬ËüÔÚ JavaFX Ö®ÉÏÌṩÁËÒ»¸ö Kotlin DSL¡£

ÈçÏÂÊǿͻ§¶ËÊӽǵÄÍêÕû¶¨Ò壺

class HelloWorldView: View("Coroutines Client UI") {
private val finder: HttpWaldoFinder by inject()
private val inputText = SimpleStringProperty("Jane")
private val resultText = SimpleStringProperty("")
override val root = form {
fieldset("Lets Find Waldo") {
field("First Name:") {
textfield().bind(inputText)
button("Search") {
action {
println("Running event handler".addThreadId())
searchForWaldo()
}
}
}
field("Result:") {
label(resultText)
}
}
}
private fun searchForWaldo() {
GlobalScope.launch(Dispatchers.Main) {
println("Doing Coroutines".addThreadId())
val input = inputText.value
val output = finder.wheresWaldo(input)
resultText.value = output
}
}
}class HelloWorldView: View("Coroutines Client UI") { private val finder: HttpWaldoFinder by inject() private val inputText = SimpleStringProperty("Jane") private val resultText = SimpleStringProperty("") override val root = form { fieldset("Lets Find Waldo") { field("First Name:") { textfield().bind(inputText) button("Search") { action { println("Running event handler".addThreadId()) searchForWaldo() } } } field("Result:") { label(resultText) } } } private fun searchForWaldo() { GlobalScope.launch(Dispatchers.Main) { println("Doing Coroutines".addThreadId()) val input = inputText.value val output = finder.wheresWaldo(input) resultText.value = output } }

ÎÒÃÇ»¹»áʹÓÃÈçϵĸ¨Öúº¯Êý×÷Ϊ String ÀàÐ͵ÄÀ©Õ¹£º

fun String.addThreadId() = "$this on thread ${Thread.currentThread().id}"

ÔÚÔËÐеÄʱºò£¬UI ÈçÏÂËùʾ£º

µ±Óû§µã»÷°´Å¥µÄʱºò£¬ÎÒÃǻᴴ½¨Ò»¸öеÄЭ³Ì£¬²¢Í¨¹ý¡°HttpWaldoFinder¡±ÀàÐ͵ķþÎñ¶ÔÏó·ÃÎÊ RESTful ¶Ëµã¡£

Kotlin Э³Ì´æÔÚÓÚÒ»¸ö¡°CoroutineScope¡±ÖУ¬¶øºóÕßÓֻᷴ¹ýÀ´ºÍijÖÖ Dispatcher ¹ØÁª£¬Dispatcher ´ú±íÁ˵ײãµÄ²¢·¢Ä£ÐÍ¡£²¢·¢Ä£ÐÍͨ³£ÊÇÒ»¸öÏ̳߳أ¬µ«ÊÇ»áÓÐËù²îÒì¡£

¾ßÌåÄÄЩ Dispatcher ¿ÉÓÃÈ¡¾öÓÚ Kotlin ´úÂëÔËÐеĻ·¾³¡£Main Dispatcher ±íʾ UI ¿âµÄʼþ´¦ÀíỊ̈߳¬Òò´Ë (ÔÚ JVM ÉÏ) Ö»ÄÜÔÚ Android¡¢JavaFX ºÍ Swing ÖÐʹÓá£×î³õ£¬Kotlin Native ¸ù±¾²»Ö§³ÖЭ³ÌµÄ¶àÏ̴߳¦Àí£¬µ«ÕâÖÖÇé¿öÕýÔڸı䡣ÔÚ·þÎñÆ÷¶Ë£¬ÎÒÃÇ¿ÉÒÔ×Ô¼ºÒýÈëЭ³Ì£¬µ«ÊÇÔÚÔ½À´Ô½¶àµÄÇé¿öÖУ¬Ð­³ÌĬÈϾÍÊÇ¿ÉÓõ쬱ÈÈçÔÚ Spring 5 ÖС£

ÔÚµ÷ÓÃ¹ÒÆð£¨suspending£©·½·¨Ö®Ç°£¬ÎÒÃDZØÐ뽫Э³Ì¡¢¡°CoroutineScope¡±ºÍ¡°Dispatche¡±×¼±¸¾ÍÐ÷¡£Èç¹ûÕâÊdzõʼµ÷Óõϰ£¨ÈçÉÏÃæµÄ´úÂëËùʾ£©£¬ÎÒÃÇ¿ÉÒÔͨ¹ý¡°Ð­³Ì¹¹ÔìÕß¡±º¯Êý£¬Èç¡°launch¡±ºÍ¡°async¡±£¬Æô¶¯¸Ã¹ý³Ì¡£

²»¹ÜÊǵ÷ÓÃЭ³Ì¹¹½¨Õߺ¯Êý£¬»¹ÊÇÏñ¡°withContext¡±ÕâÑùµÄ×÷ÓÃÓòº¯Êý£¬¶¼»á´´½¨Ò»¸öеġ°CoroutineScope¡±¡£ÔÚ¸Ã×÷ÓÃÓòÖУ¬ÈÎÎñÌåÏÖΪ¡°Job¡±ÊµÀýµÄ²ã¼¶½á¹¹¡£

ËüÃÇÓÐһЩºÜÓÐÒâ˼µÄÊôÐÔ£¬¼´£º

Job »áµÈ´ýÆä¿éÄÚËùÓÐЭ³ÌÍê³Éºó²ÅÄÜÍê³É×Ô¼º¡£

È¡ÏûÒ»¸ö Job »áµ¼ÖÂÆäËùÓеÄ×Ó Job ¶¼±»È¡Ïû¡£

×Ó Job µÄʧ°Ü»òÈ¡Ïû»á´«²¥ÖÁ¸¸ Job¡£

ÕâÑùÉè¼ÆµÄÄ¿µÄÊDZÜÃâ²¢·¢±à³ÌÖеij£¼ûÎÊÌ⣬±ÈÈçɱËÀ¸¸ Job µÄʱºòûÓÐÖÕ½áÆä×Ó Job¡£

·ÃÎÊ REST ¶ËµãµÄ·þÎñ

ÈçÏÂÊÇ HttpWaldoFinder ·þÎñµÄÍêÕû´úÂ룺

class HttpWaldoFinder : Controller(), WaldoFinder {
override suspend fun wheresWaldo(starterName: String): String {
val firstName = fetchNewName(starterName)
println("Found $firstName name".addThreadId())
val secondName = fetchNewName(firstName)
println("Found $secondName name".addThreadId())
val thirdName = fetchNewName(secondName)
println("Found $thirdName name".addThreadId())
val fourthName = fetchNewName(thirdName)
println("Found $fourthName name".addThreadId())
return fetchNewName(fourthName)
}
private suspend fun fetchNewName(inputName: String): String {
val url = URI("http://localhost:8080/wheresWaldo/$inputName")
val client = HttpClient.newBuilder().build()
val handler = HttpResponse.BodyHandlers.ofString()
val request = HttpRequest.newBuilder().uri(url).build()
return withContext<String>(Dispatchers.IO) {
println("Sending HTTP Request for $inputName".addThreadId())
client
.send(request, handler)
.body()
}
}
}

¡°fetchNewName¡±º¯Êý»á½ÓÊÕÒ»¸öÒÑÖªµÄÃû×Ö£¬²¢²éѯ¶Ëµã»ñÈ¡¹ØÁªµÄÃû×Ö¡£ÕâÊÇͨ¹ýʹÓá°HttpClient¡±ÀàÐÍÀ´ÊµÏֵ쬏ÃÀàÐÍÊÇ´Ó Java 11 ¿ªÊ¼×÷Ϊ±ê×¼µÄ¡£Êµ¼ÊµÄ HTTP GET »áÔÚÒ»¸öеÄ×ÓЭ³ÌÖÐÔËÐУ¬¸Ã×ÓЭ³Ì»áʹÓà IO Dispatcher¡£ËüµÄ±íÏÖÐÎʽÊÇÒ»¸öΪ³¤Ê±¼äÔËÐеĻ£¨ÈçÍøÂçµ÷Óã©ÓÅ»¯µÄÏ̳߳ء£

¡°wheresWaldo¡±º¯Êý»á¸ù¾ÝÃû×ÖÁ´Ö´ÐÐÎå´Î£¬ÒÔ±ãÓÚ£¨¾¡Á¦£©ÕÒµ½ Waldo¡£ÎÒÃÇËæºó½«»á·Ö½âÉú³ÉµÄ×Ö½ÚÂ룬ËùÒÔÎÒÃÇÈÃʵÏÖ¾¡¿ÉÄܵؼòµ¥¡£ÎÒÃǸÐÐËȤµÄÊÇ£¬Ã¿´Îµ÷Óá°fetchNewName¡±¶¼»áµ¼Öµ±Ç°Ð­³ÌÔÚ×ÓЭ³ÌÔËÐÐʱ±»¹ÒÆð¡£ÔÚÕâ¸öÌØÊⳡ¾°Ï£¬¸¸ Job ÔËÐÐÔÚ Main Dispatcher ÉÏ£¬¶ø×Ó Job ÔËÐÐÔÚ IO Dispatcher ÉÏ¡£Òò´Ë£¬µ±×Ó Job Ö´ÐÐ HTTP ÇëÇóʱ£¬UI ʼþ´¦ÀíÏ̻߳ᱻÊͷųöÀ´´¦ÀíÓëÊÓͼµÄÓû§½»»¥¡£ÈçÏÂͼËùʾ¡£

IntelliJ »áΪÎÒÃÇչʾºÎʱ·¢Æð¹ÒÆðµ÷Óã¬Òò´Ë»áÔÚЭ³Ì¼äת»»¿ØÖÆ¡£ÐèҪעÒ⣬Èç¹ûÎÒÃÇûÓÐÇл» Dispatcher µÄ»°£¬ÄÇ·¢Æðµ÷Óò»Ò»¶¨»á´´½¨ÐµÄЭ³Ì¡£µ±Ò»¸ö¹ÒÆðº¯Êýµ÷ÓÃÁíÒ»¸ö¹ÒÆðº¯Êýʱ£¬¿ÉÄÜ»áÔÚÏàͬµÄЭ³ÌÖмÌÐø£¬Èç¹ûÎÒÃÇÕæµÄÏ£Íû±£³ÖÔÚͬһ¸öÏß³ÌÉÏ£¬ÄÇôÕâµÄÈ·¾ÍÊÇÎÒÃÇÏëÒªµÄÐÐΪ¡£

µ±ÎÒÃÇÖ´Ðпͻ§¶ËµÄʱºò£¬ÈçϾÍÊÇ¿ØÖÆÌ¨µÄÊä³ö£º

ÎÒÃÇ¿ÉÒÔ¿´µ½£¬ÔÚÕâ¸öÌØÊâµÄ³¡¾°Ï£¬Main Dispatcher/UI ʼþ´¦ÀíÆ÷ÔËÐÐÔÚ 17 ºÅÏß³ÌÉÏ£¬¶ø IO Dispatcher ÔËÐÐÔÚÏ̳߳ØÉÏ£¬°üÀ¨ 24 ºÅºÍ 26 ºÅÏ̡߳£

¿ªÊ¼ÎÒÃǵĵ÷²é

½èÖú IntelliJ ×Ô´øµÄ×Ö½ÚÂë·Ö½â¹¤¾ß£¬ÎÒÃÇ¿ÉÒÔ¿´³öµ½µ×·¢ÉúÁËʲô¡£ÁíÍ⣬ÎÒÃÇÒ²¿ÉÒÔʹÓà JDK ÌṩµÄ±ê×¼¡°javap¡±¹¤¾ß¡£

ÎÒÃÇ¿ÉÒÔ¿´µ½¡°HttpWaldoFinder¡±µÄ·½·¨¸Ä±äÁËÆäÇ©Ãû£¬ËùÒÔËüÃÇ¿ÉÒÔ½ÓÊÜÒ»¸ö Continuation ¶ÔÏó×÷ΪÆä¶îÍâµÄ²ÎÊý£¬²¢ÇÒ·µ»ØÄ³ÖÖͨÓõĶÔÏó¡£

public final class HttpWaldoFinder extends Controller implements WaldoFinder {
public Object wheresWaldo(String a, Continuation b)
final synthetic Object fetchNewName(String a, Continuation b)
}

ÏÖÔÚ£¬ÎÒÃÇÉîÈë´úÂë¿´Ò»ÏÂÕâЩ·½·¨¶¼Ìí¼ÓÁËЩʲô£¬²¢²ûÊö¡°Continuation¡±ÊÇʲôÒÔ¼°ÏÖÔÚ·µ»ØµÄÊÇʲô¡£

Á¬Ðø´«µÝ·ç¸ñ£¨Coninuation Passing Style£¬CPS£©

°´ÕÕ Kotlin ±ê×¼»¯¹ý³Ì¶ÔЭ³ÌÌáÒéµÄÎĵµÃèÊö£¬Ð­³ÌµÄʵÏÖÊÇ»ùÓÚÁ¬Ðø´«µÝ·ç¸ñ (Continuation Passing Style£¬CPS) µÄ¡£»áÓÐÒ»¸ö continuation ¶ÔÏóÀ´´æ´¢º¯ÊýÔÚ¹ÒÆð½×¶ÎËùÐèµÄ״̬¡£

ÔÚ±¾ÖÊÉÏ£¬¹ÒÆðº¯ÊýµÄÿ¸ö¾Ö²¿±äÁ¿¶¼»á³ÉΪ continuation µÄÒ»¸ö×ֶΡ£ÁíÍ⣬»¹ÐèÒª´´½¨×Ö¶ÎÀ´´æ´¢ËùÓеIJÎÊýºÍµ±Ç°¶ÔÏó£¨Èç¹ûº¯ÊýÊÇ·½·¨µÄ»°£©¡£ËùÒÔ£¬Ò»¸ö¹ÒÆð·½·¨Èç¹ûÓÐËĸö²ÎÊýºÍÎå¸ö¾Ö²¿±äÁ¿µÄ»°£¬continuation ÖÁÉÙÒªÓÐ 10 ¸ö×ֶΡ£

ÔÚ¡°HttpWaldoFinder¡±µÄ¡°wheresWaldo¡±·½·¨ÖУ¬ÓÐÒ»¸ö²ÎÊýºÍËĸö±¾µØ±äÁ¿£¬ËùÒÔÎÒÃÇÔ¤ÆÚ continuation ʵÏÖÀàÐÍ»áÓÐÁù¸ö×ֶΡ£Èç¹ûÎÒÃǽ« Kotlin ±àÒëÆ÷Éú³ÉµÄ×Ö½ÚÂë·Ö½â³É Java Ô´ÂëµÄ»°£¬¾Í»á·¢ÏÖÊÂʵȷʵÈç´Ë£º

$continuation = new ContinuationImpl($completion) {
Object result;
int label;
Object L$0;
Object L$1;
Object L$2;
Object L$3;
Object L$4;
Object L$5;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return HttpWaldoFinder.this.wheresWaldo((String)null, this);
}
};

¼øÓÚËùÓеÄ×ֶζ¼ÊÇ Object ÀàÐ͵ģ¬ËùÒÔËüÃǸÃÈçºÎʹÓò¢²»Ã÷ÏÔ¡£Ëæ×ŽøÒ»²½Ì½Ë÷£¬ÎÒÃǽ«»á¿´µ½£º

¡°L$0¡±³ÖÓжԡ°HttpWaldoFinder¡±ÊµÀýµÄÒýÓá£ËüʼÖÕ¶¼»á´æÔÚ¡£

¡°L$1¡±³ÖÓС°starterName¡±²ÎÊýµÄÖµ¡£ËüʼÖÕ¶¼»á´æÔÚ¡£

¡°L2¡±µ½¡°L5¡±³ÖÓб¾µØ±äÁ¿µÄÖµ¡£Ëæ×Å´úÂëµÄÖ´ÐУ¬ËüÃǽ«»á½¥½øÊ½µØÌî³ä½øÀ´¡£¡°L$2¡±»á³ÖÓС°firstName¡±µÄÖµ£¬ÒÔ´ËÀàÍÆ¡£

ÎÒÃÇ»¹ÓжîÍâµÄ×ֶδ洢×îÖÕ½á¹û£¬ÓÐÒ»¸öÃûΪ¡°label¡±µÄÓÐȤµÄÕûÐÍ×ֶΡ£

¹ÒÆð»¹ÊDz»¹ÒÆð¡ª¡ªÕâÊÇÒ»¸öÎÊÌâ

µ±ÎÒÃǼì²éÉú³ÉµÄ´úÂëʱ£¬ÐèÒª¼ÇסËüÒª´¦ÀíÁ½¸ö³¡¾°¡£Ã¿µ±Ò»¸ö¹ÒÆðµÄº¯Êýµ÷ÓÃÁíÒ»¸öº¯Êýʱ£¬Ëü¿ÉÄÜ»á¹ÒÆðµ±Ç°µÄЭ³Ì£¨ÕâÑùÁíÍâÒ»¸öº¯Êý¿ÉÒÔÔÚÏàͬµÄÏß³ÌÉÏÔËÐУ©£¬Ò²¿ÉÄÜÔÚµ±Ç°Ð­³Ì»á¼ÌÐøÖ´ÐС£

ÎÒÃÇ¿¼ÂÇÒ»¸ö´ÓÊý¾Ý´æ´¢ÖжÁȡֵµÄ¹ÒÆðº¯Êý¡£µ± I/O ·¢ÉúµÄʱºò£¬ËüºÜ¿ÉÄܻᱻ¹ÒÆð£¬µ«ÊÇËüÒ²¿ÉÄܻỺ´æ½á¹û¡£ºóÐøµ÷ÓÿÉÒÔͬ²½·µ»Ø»º´æµÄÖµ£¬²»ÐèÒªÈÎºÎµÄ¹ÒÆð¡£Kotlin ±àÒëÆ÷ËùÉú³ÉµÄ´úÂë±ØÐëҪͬʱ֧³ÖÕâÁ½ÖÖ·¾¶¡£

Kotlin »áµ÷Õûÿ¸ö¹ÒÆðº¯ÊýµÄ·µ»ØÀàÐÍ£¬ÕâÑùµÄ»°£¬ËüҪô·µ»ØÕæÕýµÄ½á¹û£¬ÒªÃ´·µ»ØÌØÊâµÄÖµ COROUTINE_SUSPENDED¡£Èç¹ûÊǺóÕߵϰ£¬µ±Ç°µÄЭ³Ì»á±»¹ÒÆð¡£ÕâÒ²ÊÇΪʲô¹ÒÆðº¯ÊýµÄ·µ»ØÀàÐÍ´Ó½á¹ûÀàÐͱä³ÉÁË¡°Object¡±¡£

ÔÚÎÒÃǵÄÑùÀýÓ¦ÓÃÖУ¬¡°wheresWaldo¡±»áÖØ¸´µ÷Óá°fetchNewName¡±¡£ÔÚÀíÂÛÉÏ£¬Ã¿´ÎÕâÑùµÄµ÷Óö¼¿ÉÄÜ¹ÒÆð»ò²»¹ÒÆðµ±Ç°µÄЭ³Ì¡£°´ÕÕÎÒÃDZàд¡°fetchNewName¡±µÄ·½Ê½£¬¿ÉÒÔÖªµÀ£¬¹ÒÆðʼÖÕ¶¼»á·¢Éú¡£µ«ÊÇ£¬ÎªÁËÀí½âÉú³ÉµÄ´úÂ룬ÎÒÃDZØÐë¼ÇסËüÐèÒª´¦ÀíËùÓеĿÉÄÜÐÔ¡£

´óµÄ Switch Óï¾äºÍ Label

Èç¹ûÎÒÃǽøÒ»²½²é¿´·Ö½âºóµÄ´úÂ룬»á·¢ÏÖÒ»¸öÒþ²ØÔÚ¶à¸öǶÌ× label ÖÐµÄ switch Óï¾ä¡£ÕâÊÇÒ»¸ö״̬»úµÄʵÏÖ£¬ÓÃÀ´ÔÚ wheresWaldo() ·½·¨ÖпØÖƲ»Í¬µÄ¹ÒÆðµã¡£ÏÂÃæÊÇÕûÌåµÄ½á¹¹£º

// ³ÌÐòÇåµ¥ 1£ºÉú³ÉµÄ switch Óï¾äºÍ label
String firstName;
String secondName;
String thirdName;
String fourthName;
Object var11;
Object var10000;
label48: {
label47: {
label46: {
Object $result = $continuation.result;
var11 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch($continuation.label) {
case 0:
// Ê¡ÂÔ´úÂë
case 1:
// Ê¡ÂÔ´úÂë
case 2:
// Ê¡ÂÔ´úÂë
case 3:
// Ê¡ÂÔ´úÂë
case 4:
// Ê¡ÂÔ´úÂë
case 5:
// Ê¡ÂÔ´úÂë
default:
throw new IllegalStateException(
"call to 'resume' before 'invoke' with coroutine");
} // ½áÊø switch
// Ê¡ÂÔ´úÂë
} // ½áÊø label 46
// Ê¡ÂÔ´úÂë
} // ½áÊø label 47
// Ê¡ÂÔ´úÂë
} // ½áÊø label 48
// Ê¡ÂÔ´úÂë

ÎÒÃÇÏÖÔÚ¿ÉÒÔ¿´µ½ continuation ÖС°label¡±×ֶεÄÄ¿µÄÁË¡£µ±Íê³É¡°wheresWaldo¡±µÄ²»Í¬½×¶Îʱ£¬ÎÒÃǽ«»á±ä¸ü¡°label¡±ÖеÄÖµ¡£Ç¶Ì×µÄ label ´úÂë¿éÖаüº¬ÁËԭʼ Kotlin ´úÂëÀïÃæ¹ÒÆðµãÖ®¼äµÄ´úÂë¿é¡£Õâ¸ö¡°label¡±ÖµÔÊÐíÖØÐ½øÈë¸Ã´úÂë£¬Ìøµ½×îºó¹ÒÆðµÄµØ·½£¨Êʵ±µÄ case Óï¾ä£©£¬ÒÔ±ãÓÚ´Ó continuation ÖгéÈ¡Êý¾Ý£¬È»ºóÌø×ªµ½ÕýÈ·µÄ label ´úÂë¿é¡£

µ«ÊÇ£¬Èç¹ûËùÓÐµÄ¹ÒÆðµã¶¼Ã»ÓÐÕæÕý¹ÒÆðµÄ»°£¬Õû¸ö´úÂë¿é¿ÉÒÔͬ²½Ö´ÐС£ÔÚÉú³ÉµÄ´úÂëÖУ¬ÎÒÃǾ­³£»á¿´µ½ÕâÑùµÄƬ¶Î£º

// ³ÌÐòÇåµ¥ 2£ºÈ·¶¨µ±Ç°µÄЭ³ÌÊÇ·ñÓ¦¸Ã¹ÒÆð
if (var10000 == var11) {
return var11;
}

´ÓÉÏÃæÎÒÃÇ¿ÉÒÔ¿´µ½£¬¡°var11¡±±»ÉèÖóÉÁËCONTINUATION_SUSPENDEDµÄÖµ£¬¶ø¡°var10000¡±±£´æÁ˶ÔÁíÒ»¸ö¹ÒÆðº¯ÊýµÄµ÷Óõķµ»ØÖµ¡£Òò´Ë£¬µ±¹ÒÆð·¢Éúʱ£¬´úÂ뽫·µ»Ø (ÉÔºó»áÖØÐ½øÈë)£¬Èç¹ûûÓз¢Éú¹ÒÆð£¬Ôò´úÂ뽫ͨ¹ýÇл»µ½Êʵ±µÄ label ¿é¼ÌÐøÖ´Ðк¯ÊýµÄÏÂÒ»²¿·Ö¡£

ÔÙ´ÎÇ¿µ÷£¬Çë¼Çס£¬Éú³ÉµÄ´úÂë²»ÄܼÙÉèËùÓе÷Óö¼½«¹ÒÆð£¬»òÕßËùÓе÷Óö¼½«¼ÌÐøÊ¹Óõ±Ç°µÄЭ³Ì¡£Ëü±ØÐëÄܹ»´¦ÀíÈκοÉÄܵÄ×éºÏ¡£

¸ú×ÙÖ´ÐÐ

µ±ÎÒÃÇ¿ªÊ¼Ö´ÐÐʱ£¬continuation ÖС°label¡±µÄÖµ½«»á±»ÖÃΪÁã¡£ÈçÏÂÊǶÔÓ¦µÄ switch Óï¾ä·ÖÖ§£º

// ³ÌÐòÇåµ¥ 3£ºswitch µÄµÚÒ»¸ö·ÖÖ§
case 0:
ResultKt.throwOnFailure($result);
$continuation.L$0 = this;
$continuation.L$1 = starterName;
$continuation.label = 1;
var10000 = this.fetchNewName(starterName, $continuation);
if (var10000 == var11) {
return var11;
}
break;

ÎÒÃǽ«ÊµÀýºÍ²ÎÊý´æ´¢µ½ÁË continuation ¶ÔÏóÖУ¬È»ºó½« continuation ¶ÔÏ󴫵ݸø¡°fetchNewName¡±¡£ÈçǰÎÄËùÊö£¬±àÒëÆ÷ËùÉú³ÉµÄ¡°fetchNewName¡±°æ±¾ÒªÃ´·µ»ØÊµ¼ÊµÄ½á¹û£¬ÒªÃ´·µ»ØCOROUTINE_SUSPENDEDÖµ¡£

Èç¹ûЭ³Ì¹ÒÆðµÄ»°£¬ÄÇôÎÒÃÇ»á´Óº¯ÊýÖзµ»Ø£¬²¢ÇÒµ±ÎÒÃǻָ´µÄʱºò£¬»áÌøÈë¡°case 1¡±·ÖÖ§¡£Èç¹ûÎÒÃǼÌÐøÊ¹Óõ±Ç°µÄЭ³Ì£¬ÄÇô»áÌø³ö switch µ½Ä³Ò»¸ö label ´úÂë¿é£¬½øÈëÈçϵĴúÂ룺

// ³ÌÐòÇåµ¥ 4£ºµÚ¶þ´Îµ÷Óá°fetchNewName¡±
firstName = (String)var10000;
secondName = UtilsKt.addThreadId("Found " + firstName + " name");
boolean var13 = false;
System.out.println(secondName);
$continuation.L$0 = this;
$continuation.L$1 = starterName;
$continuation.L$2 = firstName;
$continuation.label = 2;
var10000 = this.fetchNewName(firstName, $continuation);
if (var10000 == var11) {
return var11;
}

ÒòΪÎÒÃÇÖªµÀ¡°var10000¡±°üº¬ÁËÎÒÃÇÆÚÍûµÄ·µ»ØÖµ£¬ËùÒÔÎÒÃÇ¿ÉÒÔ½«Æäת»»³ÉÕýÈ·µÄÀàÐͲ¢´æ´¢µ½¾Ö²¿±äÁ¿¡°firstName¡±ÖС£Ëæºó£¬Éú³ÉµÄ´úÂë»áʹÓá°secondName¡±À´´æ´¢Ïß³Ì id Á¬½ÓµÄ½á¹û£¬²¢ÇÒ½«Æä´òÓ¡Á˳öÀ´¡£

ÎÒÃǸüÐÂÁË continuation ÖеÄ×ֶΣ¬Ìí¼ÓÁË·þÎñÆ÷¼ìË÷µ½µÄÖµ¡£×¢Ò⣬¡°label¡±µÄÖµÏÖÔÚÒѾ­ÊÇ 2 ÁË¡£Ëæºó£¬ÎÒÃǵÚÈý´Îµ÷Óá°fetchNewName¡±¡£

µÚÈý´Îµ÷Óá°fetchNewName¡±¡ª¡ªÎÞ¹ÒÆð

ÎÒÃÇÐèÒªÔٴλùÓÚ¡°fetchNewName¡±·µ»ØµÄÖµ×ö³öÑ¡Ôñ£¬Èç¹û·µ»ØµÄÖµÊÇCOROUTINE_SUSPENDEDµÄ»°£¬ÎÒÃÇ»á´Óµ±Ç°º¯Êý·µ»Ø¡£µ±ÏÂÒ»´Îµ÷ÓÃʱ£¬ÎÒÃÇÒª×ñÑ­ switch µÄ¡°case 2¡±·ÖÖ§¡£

Èç¹ûÎÒÃǼÌÐøÊ¹Óõ±Ç°Ð­³ÌµÄ»°£¬ÄÇô½«»áÖ´ÐÐÈçϵĴúÂë¿é¡£ÎÒÃÇ¿ÉÒÔ¿´µ½ËüÓëÉÏÃæµÄ´úÂëÊÇÏàͬµÄ£¬Ö»²»¹ýÎÒÃÇÏÖÔÚÒªÓиü¶àµÄÊý¾Ý´æ´¢µ½ continuation ÖС£

// ³ÌÐòÇåµ¥ 4£ºµÚÈý´Îµ÷Óá°fetchNewName¡±
secondName = (String)var10000;
thirdName = UtilsKt.addThreadId("Found " + secondName + " name");
boolean var14 = false;
System.out.println(thirdName);
$continuation.L$0 = this;
$continuation.L$1 = starterName;
$continuation.L$2 = firstName;
$continuation.L$3 = secondName;
$continuation.label = 3;
var10000 = this.fetchNewName(secondName, (Continuation)$continuation);
if (var10000 == var11) {
return var11;
}

ÕâÖÖģʽÔÚºóÐøµÄµ÷ÓÃÖлáÖØ¸´£¨¼Ù¶¨Ê¼ÖÕûÓзµ»ØCOROUTINE_SUSPENDED£©£¬Ö±µ½µ½´ïÖÕµãΪֹ¡£

µÚÈý´Îµ÷Óá°fetchNewName¡±¡ª¡ª´øÓÐ¹ÒÆð

»òÕߣ¬Èç¹ûЭ³ÌÒѾ­±»¹ÒÆðµÄ»°£¬ÄÇô½«»áÔËÐÐÈçϵĴúÂë¿é£º

// ³ÌÐòÇåµ¥ 5£ºswitch µÄµÚÈý¸ö·ÖÖ§
case 2:
firstName = (String)$continuation.L$2;
starterName = (String)$continuation.L$1;
this = (HttpWaldoFinder)$continuation.L$0;
ResultKt.throwOnFailure($result);
var10000 = $result;
break label46;

ÎÒÃǽ« continuation ÖеÄÖµ³éÈ¡µ½º¯ÊýµÄ¾Ö²¿±äÁ¿ÖС£ËæºóʹÓÃÒ»¸ö label ÐÎʽµÄ break ʹִÐÐÌø×ªÖÁǰÊöµÄ³ÌÐòÇåµ¥ 4 ÖС£ËùÒÔ×îÖÕ£¬ÎÒÃÇ»áÔÚÏàͬµÄµØ·½½áÊø¡£

×ܽáÖ´Ðйý³Ì

ÏÖÔÚ£¬ÎÒÃÇ¿ÉÒÔÖØÐ¿´Ò»Ï³ÌÐòÇåµ¥µÄ´úÂë½á¹¹£¬²¢ÔÚÕûÌåÉÏÃèÊöÒ»ÏÂÿ¸öÇøÓòÖж¼·¢ÉúÁËʲô£º

// ³ÌÐòÇåµ¥ 6£ºÉî¶È½âÎöÉú³ÉµÄ switch Óï¾äºÍ label
String firstName;
String secondName;
String thirdName;
String fourthName;
Object var11;
Object var10000;
label48: {
label47: {
label46: {
Object $result = $continuation.result;
var11 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch($continuation.label) {
case 0:
// ½« label ÉèÖÃΪ 1£¬Èç¹û·µ»Ø¹ÒÆðµÄ»°£¬µÚÒ»´Îµ÷Óá°fetchNewName¡±
// ·ñÔò£¬´Ó switch ÖÐ break Í˳ö
case 1:
// ´Ó continuation ÖгéÈ¡²ÎÊý
// ´Ó switch ÖÐ break Í˳ö
case 2:
// ´Ó continuation ÖгéÈ¡²ÎÊýºÍµÚÒ»¸ö½á¹û
// Ìø×ªµ½Íâ±ßµÄ¡°label46¡±
case 3:
// ´Ó continuation ÖгéÈ¡²ÎÊý£¬µÚÒ»¸öºÍµÚ¶þ¸ö½á¹û
// Ìø×ªµ½Íâ±ßµÄ¡°label47¡±
case 4:
// ´Ó continuation ÖгéÈ¡²ÎÊý£¬µÚÒ»¸ö¡¢µÚ¶þ¸öºÍµÚÈý¸ö½á¹û
// Ìø×ªµ½Íâ±ßµÄ¡°label48¡±
case 5:
// ´Ó continuation ÖгéÈ¡²ÎÊý£¬µÚÒ»¸ö¡¢µÚ¶þ¸ö¡¢µÚÈý¸öºÍµÚËĸö½á¹û
// ·µ»Ø×îºóµÄ½á¹û
default:
throw new IllegalStateException(
"call to 'resume' before 'invoke' with coroutine");
} // ½áÊø switch
// ´æ´¢²ÎÊýºÍµÚÒ»¸ö½á¹ûµ½ continuation ÖÐ
// Èç¹û·µ»Ø¹ÒÆðµÄ»°£¬½« label ÉèÖÃΪ 2 ²¢¶Ô¡°fetchNewName¡±½øÐеڶþ´Îµ÷ÓÃ
// ·ñÔòµÄ»°¼ÌÐø½øÐÐ
} // ½áÊø label 46
// ´æ´¢²ÎÊý¡¢µÚÒ»¸ö½á¹ûºÍµÚ¶þ¸ö½á¹ûµ½ continuation ÖÐ
// Èç¹û·µ»Ø¹ÒÆðµÄ»°£¬½« label ÉèÖÃΪ 3 ²¢¶Ô¡°fetchNewName¡±½øÐеÚÈý´Îµ÷ÓÃ
// ·ñÔòµÄ»°¼ÌÐø½øÐÐ
} // ½áÊø label 47
// ´æ´¢²ÎÊý¡¢µÚÒ»¸ö½á¹û¡¢µÚ¶þ¸ö½á¹ûºÍµÚÈý¸ö½á¹ûµ½ continuation ÖÐ
// Èç¹û·µ»Ø¹ÒÆðµÄ»°£¬½« label ÉèÖÃΪ 4 ²¢¶Ô¡°fetchNewName¡±½øÐеÚËĴε÷ÓÃ
// ·ñÔòµÄ»°¼ÌÐø½øÐÐ
} // ½áÊø label 48
// ´æ´¢²ÎÊý¡¢µÚÒ»¸ö½á¹û¡¢µÚ¶þ¸ö½á¹û¡¢µÚÈý¸ö½á¹ûºÍµÚËĸö½á¹ûµ½ continuation ÖÐ
// ½« label ÉèÖÃΪ 5 ²¢¶Ô¡°fetchNewName¡±½øÐеÚÎå´Îµ÷ÓÃ
// ·µ»Ø×îÖÕ½á¹û»ò COROUTINE_SUSPENDED

½á¹û

Õâ¸ö´úÂë¿âÀí½âÆðÀ´²¢²»¼òµ¥¡£ÎÒÃÇ·ÖÎöÁË×Ö½ÚÂë·Ö½âµÃµ½µÄ Java ´úÂ룬ÕâЩ×Ö½ÚÂëÊÇÓÉ Kotlin ±àÒëÆ÷ÖеĴúÂëÉú³ÉÆ÷Ëù²úÉúµÄ¡£Õâ¸ö´úÂëÉú³ÉÆ÷µÄÊä³öÔÚÉè¼ÆÊ±¿¼ÂǵÄÊÇЧÂʺÍ×îС»¯£¬¶ø²»ÊÇ¿ÉÀí½âÐÔ¡£

µ«ÊÇ£¬ÎÒÃÇ¿ÉÒԵóöһЩÓÐÓõĽáÂÛ£º

²¢Ã»ÓÐʲôħ·¨¡£µ±¿ª·¢ÈËÔ±µÚÒ»´Î¿ªÊ¼Ñ§Ï°Ð­³Ìʱ£¬ºÜÈÝÒ×»áÈÏΪÓÐÐ©ÌØÊâµÄ¡°Ä§·¨¡±½«ËùÓеÄÕâЩÊÂÇéÁ¬½ÓÔÚÁËÒ»Æð¡£ÎÒÃÇ¿ÉÒÔ¿´µ½£¬Éú³ÉµÄ´úÂëֻʹÓÃÁËһЩ¹ý³Ìʽ±à³ÌµÄ»ù±¾¹¹½¨¿é£¬±ÈÈçÌõ¼þÓï¾äºÍ´øÓÐ label µÄ break¡£

ʵÏÖÊÇ»ùÓÚ continuation µÄ¡£ÔÚ×î³õµÄ KEEP ÌáÒéÖУ¬º¯Êý¹ÒÆðºÍ»Ö¸´µÄ·½·¨Êǽ«º¯ÊýµÄ״̬»º´æÔÚÒ»¸ö¶ÔÏóÖС£Òò´Ë£¬¶ÔÓÚÿ¸ö¹ÒÆðº¯Êý£¬±àÒëÆ÷½«´´½¨Ò»¸ö°üº¬ N ¸ö×Ö¶ÎµÄ continuation ÀàÐÍ£¬ÆäÖÐ N ÊDzÎÊýµÄÊýÁ¿¼ÓÉÏ×Ö¶ÎÊýÔÙ¼ÓÉÏ 3¡£×îºóµÄÈý¸ö×ֶα£´æµ±Ç°¶ÔÏó¡¢×îÖÕ½á¹ûºÍË÷Òý¡£

Ö´ÐÐʼÖÕ×ñÑ­Ò»¸ö±ê×¼µÄģʽ¡£Èç¹û´Ó¹ÒÆðÖлָ´£¬ÄÇôÎÒÃÇҪʹÓà continuation µÄ¡°label¡±×Ö¶ÎÌø×ªµ½ switch Óï¾äµÄÊʵ±·ÖÖ§¡£ÔÚÕâ¸ö·ÖÖ§ÖУ¬ÎÒÃÇ´Ó continuation ¶ÔÏó¼ìË÷µ½Ä¿Ç°ÎªÖ¹ÒѾ­ÕÒµ½µÄÊý¾Ý£¬È»ºóʹÓÃÒ»¸ö´øÓÐ label µÄ break Ìø×ªµ½Ã»Óз¢Éú¹ÒÆðµÄ´úÂ룬ÕâЩ´úÂë±¾À´ÊÇÒªÖ±½ÓÖ´Ðеġ£

 
   
2622 ´Îä¯ÀÀ       30
Ïà¹ØÎÄÕÂ

Éî¶È½âÎö£ºÇåÀíÀôúÂë
ÈçºÎ±àд³öÓµ±§±ä»¯µÄ´úÂë
ÖØ¹¹-ʹ´úÂë¸ü¼ò½àÓÅÃÀ
ÍŶÓÏîÄ¿¿ª·¢"±àÂë¹æ·¶"ϵÁÐÎÄÕÂ
Ïà¹ØÎĵµ

ÖØ¹¹-¸ÄÉÆ¼ÈÓдúÂëµÄÉè¼Æ
Èí¼þÖØ¹¹v2
´úÂëÕû½àÖ®µÀ
¸ßÖÊÁ¿±à³Ì¹æ·¶
Ïà¹Ø¿Î³Ì

»ùÓÚHTML5¿Í»§¶Ë¡¢Web¶ËµÄÓ¦Óÿª·¢
HTML 5+CSS ¿ª·¢
ǶÈëʽC¸ßÖÊÁ¿±à³Ì
C++¸ß¼¶±à³Ì