Caching internal fields in tasks

I have a task that generates a file based on some inputs, and at the same time also calculates some additional data such as a hash of the file plus various kinds of metadata. This additional data needs to be available to other tasks – preferably by simply accessing e.g. taskObject.hash – but since the data is expensive to calculate, I want to avoid recalculating it in the event that the task is up-to-date.

Take this example class:

class MyTask extends DefaultTask {
    @Input
    File source

    @Internal
    String hash = "00000000" // placeholder default value; should be overwritten if cached value exists, but left alone otherwise

    @Internal
    Integer headerStart

    @OutputFile
    File output

    MyTask() {
        // read cached hash and headerStart
    }

    @TaskAction
    def run() {
        // read source and generate output file
        hash = calculateHash(output)
        headerStart = findHeader(output)
        // save hash and headerStart
    }
}

task generateFile(type: MyTask) {
    source = project.file("source.bin")
    output = project.file("output.bin")
}

task test {
    dependsOn "generateFile"
    doLast {
        println(generateFile.hash)
    }
}

How do I best go about storing and retrieving this data? Note that in practice I have multiple kinds of tasks, all with multiple different fields I need to save, so it would be good if this could all be done automatically rather than having to write different saving code for each task.