今天要來下一步學習Kotlin中稍微進階一點的知識了,這邊會詳細說明幾個Kotlin中可能會常用到的一些類別建立,以及泛型的說明。
測驗通常會有多種題型,例如填充題或是非題。每個測驗題目都可以用一個類別代表,其中設有多種屬性。
不同的題型 (例如是非題) 可能需要用不同的資料類型代表答案。我們在此定義三種問題類型。
String
代表。Boolean
代表。Int
代表。class 類別名稱 <泛型的類型>{
// 方法或功能的撰寫
}
程式碼部分:
class Question <T>(private val questionText: String,
private val answer: T,
private val difficulty: String) {
private val TAG = "Question"
// 答題方法
fun checkQuestion(answer: T){
if (this.answer == answer){
Log.e(TAG, "checkQuestion: Correct\nquestionType: $questionText\ndifficulty: $difficulty")
} else {
Log.e(TAG, "checkQuestion: Incorrect\nquestionType: $questionText\ndifficulty: $difficulty")
}
}
}
class MainActivity : AppCompatActivity() {
// 建立題目以及標準答案,後面透過答題來呼叫function進行題目正確與否。
val question1 = Question<String>("IT_Home鐵人賽今年是第幾屆?", "15", "Easy")
val question2 = Question<Boolean>("今年是否是2023年?", true, "Easy")
val question3 = Question<Int>("今年是西元幾年?", 2023, "Easy")
val question4 = Question<Float>("圓周率的包含整數的小數點後3為數是多少?", 3.14159f, "medium")
override fun onCreate(savedInstanceState: Bundle?) {
// ... 省略
// call the function checkQuestion and pass the answer
question1.checkQuestion("15")
question2.checkQuestion(false)
question3.checkQuestion(2023)
question4.checkQuestion(3.14159f)
}
}
在MainActivity上使用的變數宣告格式如下,當中的參數設置可以往上看到我所設定的型態中只有answer是泛型
,所以這個型態必須與前面 "自己設置的型態" 一致。
val 變數名稱 = 類別名稱<自己設置的型態>("參數設置")
型態不一致產生的錯誤訊息
告知你question1
所設置的型態是Sting
但我嘗試使用Int
型態的15進行寫入。
透過使用列舉類別,就能建立內含有限數量可能值的類型。
enum class 類別名稱 {
常數名稱1, 常數名稱2, 常數名稱3 ....
}
enum class Difficulty {
EASY, MEDIUM, HARD, EXPERT
}
class Question <T>(private val questionText: String,
private val answer: T,
private val difficulty: Difficulty) {
// ... 以下省略不變
}
// 這邊只加入以下變數並取得Difficulty的列舉
private val easy = Difficulty.EASY
private val medium = Difficulty.MEDIUM
private val hard = Difficulty.HARD
private val expert = Difficulty.EXPERT
// 然後把難度的輸入改成變數
可以看到難度的文字是以常數名稱顯示。
在很多情況下,會需要讓類別裡只有一個執行個體。例如:
前面有講解過單例化模式的概念了,也就是這個物件只會建立一次,不論後續的添加修改皆無法變動其參數
var
進去讓外部可以隨時變動參數之類的,但變動後object
的參數就會一直維持更改後的數值,所以不怎麼推薦。object 類別名稱{
const val 變數名稱 = 參數或名稱
}
object StudentProgress {
const val StudentName = "IT_Home鐵人賽_Day09"
const val StudentNumber = "20230919"
}
object
這個類別的名稱,可以直接呼叫並取得當中設定的變數名稱,如下程式碼所示:class MainActivity : AppCompatActivity() {
// ... 省略
override fun onCreate(savedInstanceState: Bundle?) {
// ... 省略
// 下列這行程式碼中的 "StudentProgress.StudentName"
// 以及 "StudentProgress.StudentNumber"。
Log.e(TAG,"${StudentProgress.StudentName}(${StudentProgress.StudentNumber}):${studentAnswer} of ${questionNumber} answered." )
}
}
您可以使用「伴生物件」在其他類別裡面定義單例模式物件。
宣告伴生物件的方法
撰寫格式
這邊我直接放在Question類別中
class Question <T>(private val questionText: String,
private val answer: T,
private val difficulty: Difficulty) {
// 伴生物件建立
companion object StudentProgress{
const val StudentName = "IT_Home鐵人賽_Day09"
const val StudentNumber = "20230919"
}
// 以下方法保持不變
}
class MainActivity : AppCompatActivity() {
// ... 省略
override fun onCreate(savedInstanceState: Bundle?) {
// ... 省略
// 下列這行程式碼中的 "Question.StudentName"
// 以及 "Question.StudentNumber"。
Log.e(TAG,"${Question.StudentName}(${Question.StudentNumber}):${studentAnswer} of ${questionNumber} answered." )
}
}
// 我在原先建立的 "Question.StudentProgress" 再去定義擴充功能
// 當中的ProgressText是自訂義名稱
val Question.StudentProgress.ProgressText: String
// 再為這個擴充功能屬性定義 getter,讓它在 main() 裡回傳之前使用的同個字串。
get() = "${StudentName}(${StudentNumber})"
回到底下的function中加入 Question.ProgressText
就能得到上面所建立的輸出了。
fun Question.StudentProgress."自訂義函式名稱"(): "型態" {
// 回傳
return "$StudentName($StudentNumber)"
}
這邊建立完成後來到底下建立的function將剛剛的 Question.ProgressText
更改成建立的function名稱 Question."函式名稱"()
就好了。
如果有類別定義為資料類別,便會實作以下方法。
data class Question<T> (
val questionText: String,
val answer: T,
val difficulty: Difficulty
)
這邊稍微修改了一下程式碼:
class Quiz {
// 定義難度
private val easy = Difficulty.EASY
private val medium = Difficulty.MEDIUM
private val hard = Difficulty.HARD
private val expert = Difficulty.EXPERT
// 定義各個題型、答案、難度
val question1 = Question<String>("IT_Home鐵人賽今年是第幾屆?", "15", easy)
val question2 = Question<Boolean>("今年是否是2023年?", true, medium)
val question3 = Question<Int>("今年是西元幾年?", 2023, hard)
val question4 = Question<Float>("圓周率的包含整數的小數點後3為數是多少?", 3.14159f, expert)
// 計算題目數量
private var questionNumberArr = arrayOf(question1, question2, question3, question4)
val questionNumber = questionNumberArr.size
companion object StudentProgress{
const val StudentName = "IT_Home鐵人賽_Day09"
const val StudentNumber = "20230919"
}
private val TAG = "Question"
fun Quiz.StudentProgress.printStudentInfo (): String {
return "$StudentName($StudentNumber)"
}
// 建立方法供給外部呼叫並傳入答案
fun checkQuestion(answer: Comparable<*>, index: Int): String {
val question = questionNumberArr[index]
val result = if (question.answer == answer) {
"Correct"
} else {
"Incorrect"
}
Log.e(
TAG, "AnswerResult: $result\n" +
"questionType: ${question.questionText}\n" +
"difficulty: ${question.difficulty}\n"+
"YourAnswer: $answer\n"+
"StudentInfo: ${Quiz.printStudentInfo()}"
)
return result
}
}
class MainActivity : AppCompatActivity() {
var studentAnswer = 0
// 建立一個Quiz的物件
val quiz = Quiz()
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 建立學生答題的陣列
val studentanswers = arrayOf("15", false, 2023, 3.14159f)
// 利用for迴圈調用Quiz的checkQuestion方法來確認答題是否正確
for (i in studentanswers.indices){
if(quiz.checkQuestion(studentanswers[i],i).equals("Correct") ){
studentAnswer++
}
Log.e(TAG,"${studentAnswer} of ${quiz.questionNumber} answered." )
}
}
}
以上是今天更進階一點的Kotlin程式說明,基本上都是建立類別時可能會常常使用到的一些功能類,Kotlin中有很多類別,在建立時可能會冒出一大串選項導致眼花撩亂。